转载:http://www.2cto.com/kf/201608/535654.html

在研究了几天JNI后,在自己生成的so库中调用第三方so库时遇到问题,解决之后特意整理、记录一下。

首先说一下在网上查找资料时,对于调用第三方so库,有人说有两种方法:

1. 对于so库的API符合JNI格式(即使用javah指令生成的头文件中那种格式),可以在java代码中声明它对应的native方法,直接调 用。

比如,jni方法名为:jstringJNICALLJava_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv *,jobject); (即前缀 Java+包名+类名+方法名)

那么这个方法名就是在java中声明的native方法名:publicnative String stringFromJNI();

2. 对于so库的API不符合JNI格式,需要自己编写c/c++源文件,在该源文件实现自己的JNI格式native函数,在JNI函数中调用第三方so库的函数,再在java中调用自己实现的JNI格式的native方法。这种方法更加灵活。

一、下图是我的项目JniDemo目录:

导入的两个第三方库是:libhello.so、libhello-jni.so

自己从源文件myhello.c编译生成的库是:libmyhello.so

java中调用native方法代码如下:

packagecom.example.jnidemo;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.view.Menu;
importandroid.view.MenuItem;
importandroid.widget.TextView;
publicclassDemoMainextendsActivity {
    static{
        System.loadLibrary("myhello");
        System.loadLibrary("hello");
        System.loadLibrary("hello-jni");
    }
    TextView textView;
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo_main);
        textView = (TextView) findViewById(R.id.text);
        String str = getString() + "."+ getJNIString();
        textView.setText(str);
    }
    publicnativeString getString();
    publicnativeString getJNIString();
}

布局就一个TextView 组件 ,不再介绍。

二、源文件的编写

在使用javah生成头文件后,要在源文件中使用include“xxxx.h”引入头文件。如果头文件不在jni根目录下,还要在Android.mk中使用

LOCAL_C_INCLUDES:=(相对于jni目录的)包含头文件的目录路径

来声明一下,否则报错找不到头文件。

然后实现自己声明的native方法,再在其中调用第三方库的函数。

具体代码如下:

#include <string.h>
#include <jni.h>
#include"com_example_jnidemo_DemoMain.h"
#include"com_hello_hello_HelloActivity.h"
#include"com_example_hellojni_HelloJni.h"
jstring Java_com_example_jnidemo_DemoMain_getString(JNIEnv* env, jobject thiz) {
    //调用libhello.so中的函数
    returnJava_com_hello_hello_HelloActivity_sayHello(env, thiz);
}
jstring Java_com_example_jnidemo_DemoMain_getJNIString(JNIEnv* env,
        jobject thiz) {
//调用libhello-jni.so中的函数
    returnJava_com_example_hellojni_HelloJni_stringFromJNI(env, thiz);
}
?

三、Android.mk,Application.mk配置

Android.mk用来配置各个模块如何编译,如下:

LOCAL_PATH := $(call my-dir)  #my-dir就是该Android.mk所在目录,本项目中即jni目录
include $(CLEAR_VARS)   #清楚此行之前除了LOCAL_PATH外所有的变量,因为定义的多个模块中会有相同名称的变量,
                        #目的是避免变量赋值冲突
LOCAL_MODULE    := hello-jni    #指定一个当前模块名
LOCAL_SRC_FILES := libhello-jni.so  #要编译的源文件
include $(PREBUILT_SHARED_LIBRARY)  #编译目标,PREBUILT_表示已经编译好的,在使用NDK编译时不会再次编译,
                                    #而是直接拷贝到libs目录
                                    #预编译.a静态库使用 PREBUILT_STATIC_LIBRARY
include $(CLEAR_VARS)
LOCAL_MODULE    := hello
LOCAL_SRC_FILES := libhello.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE    := myhello  #自己由源文件编译库的模块名
LOCAL_SRC_FILES := myhello.c    #将被编译的源文件
#【重要关键点】引入依赖的第三方so库(使用模块名引入),使用\可以引入多个(注意:\符号后没有空格或其他字符)
LOCAL_SHARED_LIBRARIES := \
hello-jni\
hello
include $(BUILD_SHARED_LIBRARY) #表示编译成.so共享库,即动态库

Application.mk用来配置目标编译ABI(应用二进制接口),如arm64-v8a、armeabi、armeabi-v7a、mips、mips64、x86、x86_64。

以armeabi-v7a为例,如下:

1
APP_ABI := armeabi-v7a  #表示 编译目标 ABI(应用二进制接口) 

四、在终端使用NDK编译jni目录

如果看到所有库都install到了libs目录,没有报错,就编译成功了.

补充:下面结合我遇到过的编译错误,解析一下原因及解决手段:

前提说明:在自己的编译生成的动态库中依赖了第三方so库(编译时第三方库不会再次编译,而是直接拷贝到libs中,

所以一般是在编译依赖了第三方库的自己的动态库时报的错)

(每次用NDK重新编译,最好删除之前生成的编译结果so库和obj目录)

(1)报错error:undefined reference to'Java_com_example_hellojni_HelloJni_stringFromJNI'

collect2:error: ld returned 1 exit status

网上有人说LOCAL_ALLOW_UNDEFINED_SYMBOLS:= true就可以编译过,但这是治标不治本,运行时依然报错。

错误原因:so库在生成时,如果Application.mk声明一个变量APP-ABI:=xxx,会生成不同平台下的so库,而且编译时64位平台的so库无法在32位平台上被链接,这才报了这个解决依赖链接时找不到库中方法的问题,所以虽然Android.mk中指明了是PREBUILT_SHARED_LIBRARY的so库,但不被链接还是找不到库中API的。

解决方法:获取so库时最好要取得相应版本的库(armeabi-v7a与armeabi都是32位,一般情况下应该互相兼容,但不兼容64位的arm64-v8a)。

(2)报错 error adding symbols:File in wrong format

collect2:error: ld returned 1 exit status

(ld是链接操作)

错误原因:如果Application.mk中APP-ABI:=的目标编译平台版本为64位,而实际导入的so库版本是32位,就会不识别该so库(wrong format)。

解决方法:在Application.mk(如果没有,创建)中,把APP-ABI:=xxx的目标编译版本降低点,如armeabi-v7a、armeabi这些32位等等,使之与实际导入so库匹配

整理思路:(可以在终端中,使用$file xxx.so指令查看动态库是32位还是64位。)

接下来我通过对比不同so库与编译目标ABI来进行解析:

前提准备:在自己由源文件编译的动态库中,假设依赖调用了两个第三方so库 a.so(准备了各个ABI版本)和b.so(只有版本为armeabi的)。

1. 第三方a.so库版本arm64-v8a,(不创建Application.mk)默认目标编译版本(默认是armeabi版本):

报错:Fileformat not recognized

原因:默认的目标编译版本为32位,比第三方a.so库的64位低,识别不了a.so库。

2. 第三方a.so库版本arm64-v8a,Application.mk中目标编译版本APP-ABI:= armeabi-v7a

报错:Fileformat not recognized

原因:目标编译版本是32位,比第三方64位的a.so库低,识别不了64位a.so库。

3.第三方a.so库版本armeabi-v7a,Application.mk中目标编译版本APP-ABI:= arm64-v8a

报错:erroradding symbols: File in wrong format

原因:目标编译版本是64位比32位的第三方so库高,a.so或b.so被认为文件格式错误。

4. 第三方a.so库版本armeabi-v7a,Application.mk中目标编译版本APP-ABI:= armeabi-v7a

结果:版本匹配,NDK编译正常,armeabi-v7a兼容armeabi版本的b.so,app运行正常

5. 第三方a.so库版本armeabi,Application.mk中目标编译版本APP-ABI:= armeabi-v7a

结果:版本匹配,NDK编译正常,armeabi-v7a与armeabi互相兼容,app运行正常

6. 第三方a.so库版本arm64-v8a,Application.mk中目标编译版本APP-ABI:= arm64-v8a

报错:erroradding symbols: File in wrong format

原因:目标编译版本高于b.so,所以在解决32位的b.so的依赖时报错,但64位的a.so编译正常。

总结:

调用第三方so库时要先查看文件ABI版本,根据32或64位的相应ABI版本去定义Application.mk中目标编译版本APP-ABI。当然最好都是同一种版本,避免出现不识别、不兼容。

五、运行APP遇到的问题

补充:

我的demo是可以直接运行并调用第三方so库的,但在实际项目中还是遇到了loadLibrary()找不到so库的问题,报了下面的异常:

Couldn't load CloudService from loader dalvik.system.PathClassLoader[

DexPathList[[zip file "/data/app/com.example.demo-1.apk"],

nativeLibraryDirectories=[/data/app-lib/com.example.cameraframedatademo-1,

/vendor/lib,

/system/lib]]]: findLibrary returned null

解决方法: 既然是从DexPathList、nativeLibraryDirectories路径中找不到so库文件,那就手动push到对应目录下。我这里是把所有第三方库都adb push 到了设备的system/lib

目录下(如果需要且支持64位so库,请push到system/lib64目录下),这样项目调用最终会在system/lib目录下找到so库(由于push到了系统文件夹下,其他应用也可以调用喽)。

Android平台下JNI调用第三方so库相关推荐

  1. Android 平台下Cordova 调用Activity插件开发

    首先建立一个包名为package com.JiajiaCy.CallActivity; package com.JajaCy.CallActivity;import org.apache.cordov ...

  2. Android如何调用第三方SO库

    问题描述:Android如何调用第三方SO库: 已知条件: SO库为Android版本连接库(*.so文件),并提供了详细的接口说明: 已了解解决方案: 1.将SO文件直接放到libs/armeabi ...

  3. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    [走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 参考文章: (1)[走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 ( ...

  4. arch linux arm下载_linux系统下编译给android JNI调用的Gmssl库

    编译给android JNI调用的Gmssl库的方法,网络上并不多.按照叶大强(CSDN账号:hkNaruto)写的<GmSSL Android NDK编译 (版本95c0dba>的帖子编 ...

  5. Android平台下Dalvik层hook框架ddi的研究

    通过adbi,可以对native层的所有代码进行hook.但对于Android系统来说,这还远远不够,因为很多应用都还是在Dalvik虚拟机中运行的. 那么,有没有什么办法可以对Dalvik虚拟机中跑 ...

  6. 【Android NDK 开发】NDK 交叉编译 ( Ubuntu 中交叉编译动态库 | Android Studio 中配置使用第三方动态库 )

    文章目录 I . 动态库 与 静态库 II . 编译动态库 III. Android Studio 使用第三方动态库 IV . Android Studio 关键代码 V . 博客资源 I . 动态库 ...

  7. ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能

    ffmpeg实战教程(八)Android平台下AVfilter 实现水印,滤镜等特效功能 ffmpeg实战教程(七)Android CMake avi解码后SurfaceView显示 本篇我们在此基础 ...

  8. android平台下OpenGL ES 3.0给图片添加黑白滤镜

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  9. android平台下OpenGL ES 3.0从零开始

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  10. android平台下OpenGL ES 3.0绘制纯色背景

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

最新文章

  1. 解决在vue中axios请求超时的问题
  2. 思科asa5515端口映射_Cisco ASA端口映射
  3. 控件设置相对位置_惊人的Divi转换控件!
  4. html5 java 图片上传_java实现图片上传至服务器并显示,如何做?希望要具体的代码实现...
  5. python操作excel命令_python操作Excel读写(使用xlrd和xlrt)[转帖]
  6. E-Learning是学习系统而不是教育系统
  7. 计算机知识认证,[IT认证]计算机基础知识.ppt
  8. STM32——HAL库函数版——AD7656驱动程序
  9. C语言课程设计|通讯录管理系统(含完整代码)
  10. 转:Andriod Phone模块相关
  11. Intel 助力移动云百万 IOPS 云硬盘,打造极速云存储体验
  12. 视觉中国图片编码_学习编码第14天的应用视觉设计第4部分
  13. 六、常用的sql语句
  14. Compensating-Transaction模式
  15. 1. 设计数据库结构
  16. 蓝牙核心协议学习 -- 基带协议(Baseband)
  17. 权益证明问题 —— Proof of Stake FAQ
  18. Java秒杀系统实战系列~JMeter压力测试重现秒杀场景中超卖等问题
  19. android如何设置图标,Android APP如何设置显示图标
  20. 3GPP R18确定27个研究项目,看看包含哪些?

热门文章

  1. 计算机高程知识点,测量学复习基本知识点(全).doc
  2. The APR based Apache Tomcat Native library which allows optimal performance in......
  3. 浅析携程智能客服机器人实现
  4. Android和风SDK,Android 和风天气SDK获取天气
  5. windows系统常用命令
  6. 图片转ascii字符画C语言,将图片转为ASCII字符画
  7. 电脑网线/水晶头的连接方法(A类,B类)
  8. [转]大规模服务设计部署经验谈
  9. ESP8266_CH340G串口自动下载固件库原理
  10. win10 1607 密匙