文章大纲
- 引言
- 一、Android studio引入jar
- 二、Android Studio依赖库module
- 三、Android Studio 使用aar
- 四、Android Studio使用so文件
- 五、Eclipse项目导入到Android Studio
- 六、使用so时常见错误
- 1、**java.lang.UnsatisfiedLinkError: Couldn't load library xxxx from loader dalvik.system.PathClassLoader**
- 2、**java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader**
- 3、"java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/xxx.xx-1/base.apk"],nativeLibraryDirectories=[/data/app/xxx.xx-1/lib/arm64, /data/app/xxx.xx-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "xx.so""
- 4、还未发现...后面发现了再补充
引言
由于项目中需要用到JNI,以前虽然在Eclipse上使用过JNI和so 文件,移植到Android Studio上的时候是花费好些力气的,也处理过不少常见的错误,而且网上很多文章都是只写了大致的步骤,忽略了很多细节,为了让新手们少走弯路,同时也是加强自己的理解,把自己一步一步的操作记录下来。此系列相关文章链接:
- Android NDK——实战演练之使用Android Studio引用so库,jar包、module,aar以及导入Eclipse项目并使用JNI的正确姿势(一)
- Android NDK——实战演练之配置NDK及使用Android studio开发Hello JNI并简单打包so库(二)
- Android NDK——实战演练之App端通过串口通信完成实时控制单片机上LED灯的颜色及灯光动画特效(三)
- Android NDK——实战演练之TextureView的应用之调用外接USB摄像头自动对焦并完成隐蔽拍照(四)
一、Android studio引入jar
不同于eclipse的配置build path,Android Studio可以通过图形界面Project Structure来配置dependencies还可以通过gradle.build脚本来配置。
1、先把对应jar包copy到libs或者jniLibs下再"Add As Library"(个人推荐)
- 将jar文件复制、粘贴到app的libs或者jniLibs目录中
- 右键点击jar文件,并点击弹出菜单中的“Add As Library”,将jar文件作为类库添加到项目中
- 选择指定的类库。(高能提醒:如果不执行后两步,jar文件将不起作用,当然不能使用import语句引用。)
2、先copy再通过gradle.build脚本配置
-
将jar文件复制、粘贴到app的libs或者jniLibs目录中
-
在app下的build.gradle脚本里配置dependencies 节点(与android节点同级)
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、通过Android Studio的图形界面
Open module setting——>Project Structure——>选中对应的module——>Dependencies——>"+"——>选择对应的jar包执行完毕之后会被添加到libs文件夹下(即Project模式下的libs)
二、Android Studio依赖库module
如图module app 依赖于zklibs:
1、通过Android Studio的图形界面
通过Android Studio的图形界面也有两种形式:
- Open module setting——>Project Structure——>选中对应的module——>Dependencies——>"+"——>选择对应的jar包或者aar
- 点击 File——> New——> Import Module——>输入库模块目录的位置——>点击 Finish 则将库模块导入到项目中。
2、通过gradle.build脚本配置
2.1、首先确保库列在您 settings.gradle 文件的顶部,如下面名为“zklibs”的库所示:
include ':app', ':zklibs'
2.2、打开应用模块的 build.gradle 文件,并向 dependencies 块中添加一行新代码
//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 使用aar
aar和jar一样,本质上是一个压缩文件,相比jar文件,aar能够含带res资源文件等,application 下的module 编译之后就会被打包成apk,而library 类型的module 编译之后则会生成aar,通常文件的引入方式有两种:
1、在app module中依赖aar
1.1、通过Android Studiod 图形界面
选择File菜单或者打开Project Structure界面——>添加新的Module(New Module…)——>选择Import .JAR/.AAR Package——>选择目标aar文件导入。导入之后就会在项目根目录下会自动生成一个新的文件夹放置aar文件及其配置文件,再接着打开app module目录下的build.gradle配置文件,在dependencies依赖项中添加依赖即可
compile project(':cmoaar')
不过,这种引入方式无法查看aar文件中的代码和资源等文件。
1.2、先copy到libs目录中再手动配置gradle 脚本
1.2.1、将aar文件复制到app module目录下的libs文件夹中,然后打开app module目录下的build.gradle配置文件并在android一栏中添加依赖:
repositories {
flatDir {
dirs 'libs'
}
}
1.2.2、在dependencies一栏中添加compile(name:‘cmoaar’, ext:‘aar’)
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.crazymo.aar"
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile(name:'cmoaar', ext:'aar')
}
最后重新同步并编译工程,然后可以在app的build目录下生成对应aar的临时文件,可以看到aar文件中的jar文件、资源文件等,看上去更像是一个解压的文件夹。
2、在library module中依赖aar
当项目存在多个module时,如果想在某个library module中依赖aar文件时,那么其它所有直接或间接依赖该library module的module中都应声明该aar文件所在libs目录的相对路径或在project的build.gradle文件中进行统一配置声明,否则会同步依赖失败,除了需要按照在app module里的步骤引入之外,还需要在依赖使用这个library module 的 module下的build.gradle脚本里声明aar文件所在libs目录的相对路径,以第二种方式为例:
2.1、 将aar文件拷入想要依赖的library module下的libs文件夹中
2.2、在module或project中声明aar文件路径
- 在module中的build.gradle脚本中单独配置
在依赖aar文件的library module的build.gradle文件中配置如下:
android {
compileSdkVersion 28
///1
repositories {
flatDir {
dirs 'libs'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation(name:'csj_sdk',ext:'aar')
}
还需在其它所有直接或间接依赖该library module的其他module中的build.gradle文件配置:
repositories {
flatDir {
dirs '../xxx/libs','libs' //将xxx代表 引入aar文件的lib module名
}
}
- 在project的build.gradle脚本中统一配置
allprojects {
repositories
flatDir {
dirs project(':xxx').file('libs') //将xxx替换为引入aar文件的module名
}
}
2.3、在引入改aar的 lib module下的build.gradle脚本使用implementation(name:‘csj_sdk’,ext:‘aar’)或者api(name:‘csj_sdk’,ext:‘aar’)引入依赖。
四、Android Studio使用so文件
前面一篇Android NDK——配置NDK及使用Android studio开发Hello JNI并简单打包SO介绍了so文件,它是unix的动态连接库,是二进制文件,其本质就是本地语言(c/c++)程序文件,作用相当于windows下的.dll文件。而在Android中调用动态库文件(*.so)都是通过jni的方式。
1、引入so文件到项目中
我们都知道Android Studio的项目结构与在Eclipse里的区别巨大,切换为Project模式和Android模式,显示的结构都有所不同,这也导致很多初学者有点迷了,当然也包括我,走过不少弯路,Google、StackOverFlow走了很多遍,折腾了一番,最后终于成功了,只需两步骤。
-
把Android Studio 里的项目且为Project类型的结构,在xxx/src/main的目录下下新建名为 ”jniLibs“ 文件夹(注意大小写,与java文件夹同级)
-
再将so文件复制、粘贴到“jniLibs”目录内。(其实jniLibs文件里不仅仅可以放置so文件、也可以放置jar包类型的库)不需要再额外去配置Gradle了
//当然还有另一种引入so,就是放到libs下,我不喜欢用这种方式。。。
/**如果使用jniLibs文件夹导入so文件,不需要在gradle中配置了;如果将so文件添加在module的libs文件夹下,则需要在module的gradle配置中添加一下配置*/
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
2、定义自己的本地jni接口类
2.1、获取so里定义的本地方法签名
借助是是Linux的一个命令:nm -D xxxx.so还可以设置-D以外的其他参数,不过-D已经足够
nm -D libelia.so
下图显示的就是联发科SmartLink方案的so库定义的方法签名还有其他信息,就不贴了
2.2、实现自己的本地jni接口类
把所要使用的so文件复制粘贴到”jniLibs“文件夹之后,一般来说其他第三方的开放平台的so文件都是已经把对应的本地Java接口类一起封装到so或者其他库文件里了,我们不需要自己去定义自己的本地接口类,假如说第三方只是提供了so文件,那么就需要我们去定义jni接口类(这个类并不能是随意的,必须是和so文件里定义的方法名的一一对应,即包名和类名必须一致,否则会发生编译通过加载的时候就出错)
假如so里是这样定义本地方法,那么对应的我们这个本地接口类,必须满足四个条件:
-
包名是crazymo.train.jnitraining
-
类名是MainActivity
-
定义的方法名为 helloJni
-
返回值类型为String
那么定义这个本地接口方法类的一般步骤是:
- 在项目里首先创建一个对应的包
- 再这个包里创建对应的公开类
- 最后在这个类里定义对应的本地接口方法(常规修饰符 native static 返回值类型 helloJni**当然static并不是必须的)
3、加载so文件
加载so文件很简单,如果你这个APP必须依赖于这个so才能运行的话,建议可以在自己的Application去实现
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库文件,不要把前面的lib添加进来
}
public native String helloJni();
}
4、利用本地jni接口类调用对应的接口方法
这个更简单了,就和我们普通java类的调用语法一样,如果是静态的就用类去调用,如果非静态则用对应的实例去调用,至于怎么调用到本地代码的,那部分工作由系统会根据你本地接口的包名、方法名去找到对应的C/C++代码,所以本地接口类往往是我们使用so时发生错误的罪魁祸首之一
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、简单使用so库项目的结构图
五、Eclipse项目导入到Android Studio
1、普通Eclipse导入Android Studio
普通的导入流程很简单,有两个入口:直接在打开Android studio的窗口中选择"import project(Eclipse ADT ,gradle,etc)"然后按步骤导入即可(进到这个入口也很简单,把Android studio其他的Project 窗口都关闭了,只留下一个Project然后“Close Project”即可)
或者在已经打开的Project窗口中,切换到Project视图——>在Project跟目录上右键——>Module——>import Eclipse ADT project
2、JNI Eclipse 项目导入到Android Studio
导入JNI Eclipse项目时,前面的步骤都一样,导入完成之后,还得通过选中Module——>右键“Link C++ Project with Gradle”配置C++ Link——>可以选ndk-build——>找到Android.mk——>点击Ok (或者CMake——>选中CMakeList.txtk——>点击Ok),否则会本地代码会报错。
3、Eclipse项目导入到Android Studio的常见错误
3.1、编码错误
比如说Eclise项目下的编码为UTF-8,而Android Studio下的默认为UTF-8 无BOM 格式,此时只需要把Eclipse下的编码改为UTF-8 无BOM即可解决以下错误
3.2、未配置Link C++ Project with Gradle
六、使用so时常见错误
1、java.lang.UnsatisfiedLinkError: Couldn’t load library xxxx from loader dalvik.system.PathClassLoader
导致这个异常的根本原因就是系统在本地方法与我们本地方法接口类无法对应上,官方一点就是JVM找不到native method的native
- 还未加载对应的so导致的Crash!xxxcouldn’t find “xxx.so”,因为apk打包安装时,系统会把apk中libs目录下armeabi的so拷贝到应用的私有目录下
Crash!java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/xxx],nativeLibraryDirectories=[/vendor/lib, /systemb]]] couldn’t find “xxx.so”
-
加载的so与所运行的设备的abi架构不一致,只要在在对应的文件夹里添加上相应的so文件即可
-
java.lang.UnsatisfiedLinkError:No implementation found for XXX
这种错误一般来就是我们本地方法接口类没有和c/c++里的方法对应上
2、java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader
原因是引用了多方的so,很常见的情况是libaxx.so在各个架构对应的文件夹中都存在,而另一个libcxx.so只存在于32位对应的armaebi文件下,其他架构的都没有,那么此时程序运行在非armaebi架构的设备时则会直接报错强退。错误的日志如下:
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
解决方法:
最佳的方案肯定是添加上对应的so到对应的文件夹下,不过由于某些原因,不能找到对应的so库,也可以采用投机取巧的方式,把armaebi下的copy到其他文件下或者删除其他的文件夹,总之,要保证你有我也有,不能你有我无。
3、“java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/xxx.xx-1/base.apk”],nativeLibraryDirectories=[/data/app/xxx.xx-1/lib/arm64, /data/app/xxx.xx-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn’t find “xx.so””
有可能是架构不支持