Android之.so文件奇巧淫技
.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詳解 ,這里只做一個簡單的環境搭建。
- 下載NDK
在Android Studio上下載即可。下載完后可以在structs目錄查看。
- 打開項目根目錄的local.properties文件
- 打開項目根目錄的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文件。
來自:https://yq.aliyun.com/articles/71696?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&201738&utm_content=m_12787