Android NDK——使用Android Studio引用so库,jar包、module以及导入Eclipse项目并使用JNI的正确姿势

引言

由于项目中需要用到JNI,以前虽然在Eclipse上使用过JNI和SO 文件,移植到Android Studio上的时候是花费好些力气的,也处理过不少常见的错误,而且网上很多文章都是只写了大致的步骤,忽略了很多细节,为了让新手们少走弯路,同时也是加强自己的理解,把自己一步一步的操作记录下来。

一、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的图形界面

Open module setting——>Project Structure——>选中对应的module——>Dependencies——>"+"——>选择对应的jar包

2、通过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开发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里是这样定义本地方法,那么对应的我们这个本地接口类,必须满足四个条件:

  1. 包名是crazymo.train.jnitraining

  2. 类名是MainActivity

  3. 定义的方法名为 helloJni

  4. 返回值类型为String

那么定义这个本地接口方法类的一般步骤是:

  1. 在项目里首先创建一个对应的包
  2. 再这个包里创建对应的公开类
  3. 最后在这个类里定义对应的本地接口方法(常规修饰符 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库项目的结构图

这里写图片描述

四、NDK调试

默认情况下是不支持NDK调试的,但我们只要做些简单配置即可实现支持。

1、打开JNI调试 openModuleSettings——>选中module——>Build Types——>Jni Debuggable为true——Apply

这里写图片描述

2、配置Android Native - Debugger run——>Edit configurations——>选中对应的module——>Debugger——>Debugger Type 选native——Apply

这里写图片描述

3、下载安装LLDB,Done。

五、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、还未发现...后面发现了再补充

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容