引言
由于項(xiàng)目中需要用到JNI,以前雖然在Eclipse上使用過(guò)JNI和SO 文件,移植到Android Studio上的時(shí)候是花費(fèi)好些力氣的,也處理過(guò)不少常見(jiàn)的錯(cuò)誤,而且網(wǎng)上很多文章都是只寫(xiě)了大致的步驟,忽略了很多細(xì)節(jié),為了讓新手們少走彎路,同時(shí)也是加強(qiáng)自己的理解,把自己一步一步的操作記錄下來(lái)。
一、Android studio引入jar
不同于eclipse的配置build path,Android Studio可以通過(guò)圖形界面Project Structure來(lái)配置dependencies還可以通過(guò)gradle.build腳本來(lái)配置。
1、先把對(duì)應(yīng)jar包c(diǎn)opy到libs或者jniLibs下再"Add As Library"(個(gè)人推薦)
- 將jar文件復(fù)制、粘貼到app的libs或者jniLibs目錄中
- 右鍵點(diǎn)擊jar文件,并點(diǎn)擊彈出菜單中的“Add As Library”,將jar文件作為類庫(kù)添加到項(xiàng)目中
- 選擇指定的類庫(kù)。(高能提醒:如果不執(zhí)行后兩步,jar文件將不起作用,當(dāng)然不能使用import語(yǔ)句引用。)
2、先copy再通過(guò)gradle.build腳本配置
將jar文件復(fù)制、粘貼到app的libs或者jniLibs目錄中
在app下的build.gradle腳本里配置dependencies 節(jié)點(diǎn)(與android節(jié)點(diǎn)同級(jí))
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')//**主要是這兩句
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile files('libs/konke-android-lib.jar')//**編譯konke-android-lib.jar
}
3、通過(guò)Android Studio的圖形界面
Open module setting——>Project Structure——>選中對(duì)應(yīng)的module——>Dependencies——>"+"——>選擇對(duì)應(yīng)的jar包執(zhí)行完畢之后會(huì)被添加到libs文件夾下(即Project模式下的libs)
二、Android Studio依賴module
如圖module app 依賴于zklibs:
1、通過(guò)Android Studio的圖形界面
Open module setting——>Project Structure——>選中對(duì)應(yīng)的module——>Dependencies——>"+"——>選擇對(duì)應(yīng)的jar包
2、通過(guò)gradle.build腳本配置
//app的gradle
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0'
compile 'com.google.code.gson:gson:2.7'
compile project(path: ':zklibs')//主要是這一句
}
三、Android Studio使用SO文件
前面一篇Android NDK——配置NDK及使用Android studio開(kāi)發(fā)Hello JNI并簡(jiǎn)單打包SO介紹了so文件,它是unix的動(dòng)態(tài)連接庫(kù),是二進(jìn)制文件,其本質(zhì)就是本地語(yǔ)言(c/c++)程序文件,作用相當(dāng)于windows下的.dll文件。而在Android中調(diào)用動(dòng)態(tài)庫(kù)文件(.so)都是通過(guò)jni的方式*。
1、引入so文件到項(xiàng)目中
我們都知道Android Studio的項(xiàng)目結(jié)構(gòu)與在Eclipse里的區(qū)別巨大,切換為Project模式和Android模式,顯示的結(jié)構(gòu)都有所不同,這也導(dǎo)致很多初學(xué)者有點(diǎn)迷了,當(dāng)然也包括我,走過(guò)不少?gòu)澛罚珿oogle、StackOverFlow走了很多遍,折騰了一番,最后終于成功了,只需兩步驟。
把Android Studio 里的項(xiàng)目且為Project類型的結(jié)構(gòu),在xxx/src/main的目錄下下新建名為 ”jniLibs“ 文件夾(注意大小寫(xiě),與java文件夾同級(jí))
再將so文件復(fù)制、粘貼到“jniLibs”目錄內(nèi)。(其實(shí)jniLibs文件里不僅僅可以放置so文件、也可以放置jar包類型的庫(kù))不需要再額外去配置Gradle了
//當(dāng)然還有另一種引入so,就是放到libs下,我不喜歡用這種方式。。。
/**如果使用jniLibs文件夾導(dǎo)入so文件,不需要在gradle中配置了;如果將so文件添加在module的libs文件夾下,則需要在module的gradle配置中添加一下配置*/
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
2、定義自己的本地jni接口類
2.1、獲取so里定義的本地方法簽名
借助是是Linux的一個(gè)命令:nm -D xxxx.so還可以設(shè)置-D以外的其他參數(shù),不過(guò)-D已經(jīng)足夠
nm -D libelia.so
下圖顯示的就是聯(lián)發(fā)科SmartLink方案的so庫(kù)定義的方法簽名還有其他信息,就不貼了
2.2、實(shí)現(xiàn)自己的本地jni接口類
把所要使用的so文件復(fù)制粘貼到”jniLibs“文件夾之后,一般來(lái)說(shuō)其他第三方的開(kāi)放平臺(tái)的so文件都是已經(jīng)把對(duì)應(yīng)的本地Java接口類一起封裝到so或者其他庫(kù)文件里了,我們不需要自己去定義自己的本地接口類,假如說(shuō)第三方只是提供了so文件,那么就需要我們?nèi)ザxjni接口類(這個(gè)類并不能是隨意的,必須是和so文件里定義的方法名的一一對(duì)應(yīng),即包名和類名必須一致,否則會(huì)發(fā)生編譯通過(guò)加載的時(shí)候就出錯(cuò))
假如so里是這樣定義本地方法,那么對(duì)應(yīng)的我們這個(gè)本地接口類,必須滿足四個(gè)條件:
包名是crazymo.train.jnitraining
類名是MainActivity
定義的方法名為 helloJni
返回值類型為String
那么定義這個(gè)本地接口方法類的一般步驟是:
- 在項(xiàng)目里首先創(chuàng)建一個(gè)對(duì)應(yīng)的包
- 再這個(gè)包里創(chuàng)建對(duì)應(yīng)的公開(kāi)類
- 最后在這個(gè)類里定義對(duì)應(yīng)的本地接口方法(常規(guī)修飾符 native static 返回值類型 helloJni**當(dāng)然static并不是必須的)
3、加載so文件
加載so文件很簡(jiǎn)單,如果你這個(gè)APP必須依賴于這個(gè)so才能運(yùn)行的話,建議可以在自己的Application去實(shí)現(xiàn)
System.loadLibrary("helloJni");//加載so文件,不要帶上前綴lib和后綴.so
package crazymo.train.jni;
/**
* @auther: Crazy.Mo
* Date: 2016/10/13
* Time:15:22
* Des:
*/
public class HelloJNI {
static {
System.loadLibrary("helloJni");//引入你的so庫(kù)文件,不要把前面的lib添加進(jìn)來(lái)
}
public native String helloJni();
}
4、利用本地jni接口類調(diào)用對(duì)應(yīng)的接口方法
這個(gè)更簡(jiǎn)單了,就和我們普通java類的調(diào)用語(yǔ)法一樣,如果是靜態(tài)的就用類去調(diào)用,如果非靜態(tài)則用對(duì)應(yīng)的實(shí)例去調(diào)用,至于怎么調(diào)用到本地代碼的,那部分工作由系統(tǒng)會(huì)根據(jù)你本地接口的包名、方法名去找到對(duì)應(yīng)的C/C++代碼,所以本地接口類往往是我們使用so時(shí)發(fā)生錯(cuò)誤的罪魁禍?zhǔn)字?/p>
package crazymo.train.jni;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView)findViewById(R.id.txt_usejni)).setText( new HelloJNI().helloJni());//使用jni方法
}
}
5、簡(jiǎn)單使用so庫(kù)項(xiàng)目的結(jié)構(gòu)圖
四、NDK調(diào)試
默認(rèn)情況下是不支持NDK調(diào)試的,但我們只要做些簡(jiǎn)單配置即可實(shí)現(xiàn)支持。
1、打開(kāi)JNI調(diào)試 openModuleSettings——>選中module——>Build Types——>Jni Debuggable為true——Apply
2、配置Android Native - Debugger run——>Edit configurations——>選中對(duì)應(yīng)的module——>Debugger——>Debugger Type 選native——Apply
3、下載安裝LLDB,Done。
五、Eclipse項(xiàng)目導(dǎo)入到Android Studio
1、普通Eclipse導(dǎo)入Android Studio
普通的導(dǎo)入流程很簡(jiǎn)單,有兩個(gè)入口:直接在打開(kāi)Android studio的窗口中選擇"import project(Eclipse ADT ,gradle,etc)"然后按步驟導(dǎo)入即可(進(jìn)到這個(gè)入口也很簡(jiǎn)單,把Android studio其他的Project 窗口都關(guān)閉了,只留下一個(gè)Project然后“Close Project”即可)
或者在已經(jīng)打開(kāi)的Project窗口中,切換到Project視圖——>在Project跟目錄上右鍵——>Module——>import Eclipse ADT project
2、JNI Eclipse 項(xiàng)目導(dǎo)入到Android Studio
導(dǎo)入JNI Eclipse項(xiàng)目時(shí),前面的步驟都一樣,導(dǎo)入完成之后,還得通過(guò)選中Module——>右鍵“Link C++ Project with Gradle”配置C++ Link——>可以選ndk-build——>找到Android.mk——>點(diǎn)擊Ok (或者CMake——>選中CMakeList.txtk——>點(diǎn)擊Ok),否則會(huì)本地代碼會(huì)報(bào)錯(cuò)。
3、Eclipse項(xiàng)目導(dǎo)入到Android Studio的常見(jiàn)錯(cuò)誤
3.1、編碼錯(cuò)誤
比如說(shuō)Eclise項(xiàng)目下的編碼為UTF-8,而Android Studio下的默認(rèn)為UTF-8 無(wú)BOM 格式,此時(shí)只需要把Eclipse下的編碼改為UTF-8 無(wú)BOM即可解決以下錯(cuò)誤
3.2、未配置Link C++ Project with Gradle
六、使用so時(shí)常見(jiàn)錯(cuò)誤
1、java.lang.UnsatisfiedLinkError: Couldn't load library xxxx from loader dalvik.system.PathClassLoader
導(dǎo)致這個(gè)異常的根本原因就是系統(tǒng)在本地方法與我們本地方法接口類無(wú)法對(duì)應(yīng)上,官方一點(diǎn)就是JVM找不到native method的native
- 還未加載對(duì)應(yīng)的so導(dǎo)致的Crash!xxxcouldn’t find “xxx.so”,因?yàn)閍pk打包安裝時(shí),系統(tǒng)會(huì)把a(bǔ)pk中l(wèi)ibs目錄下armeabi的so拷貝到應(yīng)用的私有目錄下
Crash!java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/xxx],nativeLibraryDirectories=[/vendor/lib, /systemb]]] couldn’t find “xxx.so”
加載的so與所運(yùn)行的設(shè)備的abi架構(gòu)不一致,只要在在對(duì)應(yīng)的文件夾里添加上相應(yīng)的so文件即可
java.lang.UnsatisfiedLinkError:No implementation found for XXX
這種錯(cuò)誤一般來(lái)就是我們本地方法接口類沒(méi)有和c/c++里的方法對(duì)應(yīng)上
2、java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader
原因是引用了多方的so,很常見(jiàn)的情況是libaxx.so在各個(gè)架構(gòu)對(duì)應(yīng)的文件夾中都存在,而另一個(gè)libcxx.so只存在于32位對(duì)應(yīng)的armaebi文件下,其他架構(gòu)的都沒(méi)有,那么此時(shí)程序運(yùn)行在非armaebi架構(gòu)的設(shè)備時(shí)則會(huì)直接報(bào)錯(cuò)強(qiáng)退。錯(cuò)誤的日志如下:
10-28 15:42:28.122 5307-5307/com.xiaoi.app.zkSmartHome E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xiaoi.app.zkSmartHome, PID: 5307
java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader[DexPathList[[dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-zxing_c557fb7a8d7e6e337af354ce06614692a32b946a-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-support-annotations-24.0.0_abdd7eb84ec5507286f957f2abccaca254128b0c-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-rxjava-1.1.8_75fd2ee9fdad54b1b788e8d01c74e78698f28eae-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-retrofit-2.0.0-beta4_3efd0604843b4a6440028ce43f72e5845c1c3325-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-picasso-2.5.2_badcc59626c8bf60fbd570ba883ac0f8d5c9be7a-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-okio-1.6.0_c6c36c9266a53bff725e5087f6a3090b1d0ab593-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-okhttp-3.0.1_a35a122a63f63f6d2b3ba59d028c055fab521b52-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-io.reactivex-rxandroid-1.2.1_6e88671f81f408ad9e58406d59bc0cda6a6af625-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-internal_impl-24.0.0_1ca3cb52067dc09725d551b03ece99cd965979ac-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-in.srain.cube-ultra-ptr-1.0.11_b0a09794d2bb3bfed3ce82634bdccabed79fc5d0-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-gson-2.4_1cef8cfc76ca82a728656c88394ab94c85c46ee1-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-glide-3.6.1_f81c2f329f31a6fbb9641a61098e423c033cd42e-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-converter-gson-2.0.0-beta4_75a1a6273cb28d11375dfff6cd0aa45f11079258-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.orhanobut-logger-1.3_89736aa22bffa06d17995d9ad26acdfaf3572df7-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.android.support-support-vector-drawable-24.0.0_8d5d9e2412dc464146da0fdb00638a8cb0b0130d-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.android.support-support-v4-24.0.0_225ce4463e0d8c3e77ccfd8c1e749bd698e46fcc-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.android.support-recyclerview-v7-24.0.0_39a4b7cd3d134a80b92025fdd19f175953aa0dcc-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.android.support-cardview-v7-24.0.0_22b22b962be76ccc27cc64fad5c53d30515f6535-classes.dex", dex file "/data/data/com.xiaoi.app.zkSmartHome/files/instant-run/dex/slice-com.android.support-appcompat-v7-24.0.0_4ce805b4f9e08926ae1
解決方法:
最佳的方案肯定是添加上對(duì)應(yīng)的so到對(duì)應(yīng)的文件夾下,不過(guò)由于某些原因,不能找到對(duì)應(yīng)的so庫(kù),也可以采用投機(jī)取巧的方式,把a(bǔ)rmaebi下的copy到其他文件下或者刪除其他的文件夾,總之,要保證你有我也有,不能你有我無(wú)。