Android NDK——使用Android Studio引用so库,jar包及module并使用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。

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

时间: 2024-10-14 00:16:44

Android NDK——使用Android Studio引用so库,jar包及module并使用JNI的正确姿势的相关文章

com.android.dex.DexException: Multiple dex files define(jar包重复引用) 错误解决

前段时间开始转入Android studio,不料果真使用时候遇到些错误,在此记下! 出现这个错误往往是在libs目录下有个jar包,然后在gradle文件中又引用了,即: 共同引用了. 解决方法: 1.既然在gradle文件中引用了,即2,那么把libs中的删了. 2.更改1中的配置,即把compile改为provided,更改方法可以手动更改,也可以在structure中更改,即: 出现上面错误还有一种可能就是项目需要引用其它项目作为一个library,而你自己的项目也引用了该jar包,这样

android studio 使用gradle 导出jar包,并打包assets目录

最近项目在做一个sdk,供别的开发者使用,所以要求导出jar包. 与eclipse不同,android studio 1.0 没提供导出jar包的图形界面.需要结合gradle来生成jar包. 首先 需要设置module应用的gradle插件为 library 代码长这样: ? 1 <code class="hljs">apply plugin: 'com.android.library'</code> 这样,build的时候,android studio 1.

Android 将Activity及其他类打包成jar包供第三方调用

在开发java工程时,一个项目可能分为多个模块,为了实现模块间的解耦和独立,提高模块的复用性,通常将项目按模块分为多个java工程进行开发,最后通过jar包等工程依赖的方式实现系统集成,提高模块的耦合和复用. 现在开发Android项目通过实践和总结,发现这种方式特别有必要,比如开发一个android端的视频播放功能,肯定有播放和下载模块,如果不分开放在一个工程里面不断的添加新的功能,产品的每一个研发都不断的添加修改功能,最后维护越来越难,bug越来越多,并相互推诿,这种方式能避免这种情况,此为

Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法

方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/commons-httpclient-3.1.jar lib/commons-logging-1.1.jar lib/log4j-1.

将引用了第三方jar包的Java项目打包成jar文件的两种方法

方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/commons-httpclient-3.1.jar lib/commons-logging-1.1.jar lib/log4j-1.

(转载)Eclipse将引用了第三方jar包的Java项目打包成可执行jar的两种方法

转载自:http://www.cnblogs.com/lanxuezaipiao/p/3291641.html 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 "MANIFEST.MF", 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/common

android studio中导入第三方jar包和第三方库文件的方法

一.导入第三方jar包的方法 其实较为简单,以下步骤: 1>在工程的libs下面放置需要导入的jar包 2>在导入的jar包右键,选择"add as library" 3>这时候就能够在app下面的build.gradle中发现多了如下说明:"compile XXXX",说明导入jar文件成功了. 二.导入第三方类库文件 相对而言,其实就是将之前的Eclipse的project或者module转化成android studio下的可执行的proje

解决Android Studio加载第三方jar包,出现包重复加载的问题:

通过Maven中央库添加第三方jar包的时候,出现了重复加载jar包的问题,解决办法很简单去掉一个不让它去加载就OK了 一.错误 Error:Execution failed for task ':app:dexDebug'. > com.android.ide.common.internal.LoggedErrorException: Failed to run command: F:\zsl\sdk\build-tools\21.1.2\dx.bat --dex --output F:\zs

android studio打可执行jar包

android studio可以通过library工程打出jar包 解压会看到META-INF/MANIFEST.MF文件的打开如下: Manifest-Version: 1.0 增加一行,注意冒号后面有一个空格,如下: Manifest-Version: 1.0 Main-Class: com.example.MyClass 在jar解压后的文件夹下 按shift+鼠标右键 选择:在此处打开命令窗口 ,在cmd中执行如下命: jar -cvmf META-INF\manifest.mf tes