Android之.so文件奇巧淫技

MyrtleV18 7年前發布 | 6K 次閱讀 JNI 安卓開發 Android開發 移動開發

.so文件的前世今生

早期的Android系統幾乎只支持ARMv5的CPU架構,而現在它可以支持7種,幾乎涵蓋了市面上大部分的CPU架構。

Android系統目前支持的CPU架構主要包含以下7種:ARMv5,ARMv7 (從2010年起),x86 (從2011年起),MIPS (從2012年起),ARMv8,MIPS64和x86_64 (從2014年起),每一種都關聯著一個相應的ABI。

二進制接口(ABI)

應用程序二進制接口(Application Binary Interface)定義了二進制文件(尤其是.so文件)如何運行在相應的系統平臺上,從使用的指令集,內存對齊到可用的系統函數庫。在Android中調用動態庫文件(*.so)都是通過jni的方式。形如我們常見的:

void System.load(String pathName);

在Android系統上,每一個CPU架構對應一個ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

比較常見的百度地圖等sdk一般都會提供好幾套的架構庫。

Android平臺生成.so文件

說了這么多,那么如何在Android平臺上生成.so文件呢?

NDK環境搭建

關于ndk詳細的理論請查看之前的講解 ndk詳解 ,這里只做一個簡單的環境搭建。

  1. 下載NDK

在Android Studio上下載即可。下載完后可以在structs目錄查看。

  1. 打開項目根目錄的local.properties文件

  1. 打開項目根目錄的gradle.properties文件,添加
android.useDeprecatedNdk=true

這樣ndk環境就搭建好了。

編寫代碼

1 . 創建一個Java類,以實現jni調用。

public class JniUtil {
    static {
        //括號的參數可以任意修改
        System.loadLibrary("jniutil");
    }

//java調C/C++中的方法都需要用native聲明且方法名必須和C/C++的方法名一樣
public native String test();

}</code></pre>

然后Make Project,完成后便生成字節碼文件。

2 . 根據JniUtil.class生成.h文件

打開Android Studio的Terminal,執行以下命令:

javah -d jni -classpath 編譯后的class文件的絕對路徑

執行上述命令后便會在app/src/main目錄下自動創建一個包含.h文件的jni文件夾。

我們直接打開文件:

#include <jni.h>

ifndef _Included_com_othershe_jnitest_JniUtil

define _Included_com_othershe_jnitest_JniUtil

ifdef __cplusplus

extern "C" {

endif

JNIEXPORT jstring JNICALL Java_com_othershe_jnitest_JniUtil_test (JNIEnv *, jobject);

ifdef __cplusplus

}

endif

endif</code></pre>

3 . 編寫.c文件(jniutil.c)

這里的jniutil文件名需要和JniUtil類中System.loadLibrary("jniutil");的參數一致。

jniutil.c具體的編寫可根據自己的業務實現,這里僅做測試:

#include "com_othershe_jnitest_JniUtil.h"

JNIEXPORT jstring JNICALL Java_com_othershe_jnitest_JniUtil_test
        (JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "jni調用成功");
}

注:這里需要注意包名的統一。

4 . 配置項目app目錄下的build.gradle文件

defaultConfig {
        applicationId "com.othershe.jnitest"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"

        ndk {
            moduleName "jniutil"
            abiFilters 'armeabi', 'x86', 'armeabi-v7a' //輸出指定三種abi體系結構下的so庫。
        }
    }

其中ndk標簽是新添加的,moduleName 的值同樣為System.loadLibrary("jniutil");的參數。由于配置了abiFilters,則只會得到armeabi、x86、armeabi-v7a三種ABI對應的.so文件。

最后還需要在生成的jni文件夾下創建一個空的util.c文件,否則會有如下異常:

完成以上操作后,jni文件的目錄如下:

生成.so文件

其實到這一步就已經完成了,那么我們怎么驗證我們是否成功的創建了.so文件呢?現在我們來測試一下,寫個TextView顯示一下調用的C:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = (TextView)findViewById(R.id.tv);

        tv.setText(new JniUtil().getString());
    }
}

使用.so文件需要注意的地方

當你編譯.so文件時,經常會出現一些錯誤,其中最多的是"UnsatisfiedLinkError","dlopen: failed"以及其他類型的crash或者低下的性能:

高版本編譯的.so文件運行在低版本手機上

NDK平臺不是向后兼容的,而是向前兼容的,推薦使用app的minSdkVersion對應的編譯平臺。

每個支持的CPU架構都需要一套對應的.so文件

這個就好比32位的軟件沒辦法運行在64位的CPU上,必須為每一個CPU架構提供一套.so文件。

參考: Android中.so文件的Hook

 

來自:https://yq.aliyun.com/articles/71696?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&201738&utm_content=m_12787

 

 本文由用戶 MyrtleV18 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!