首先说一下在网上查找资料时,对于调用第三方so库,有人说有两种方法:
1. 对于so库的API符合JNI格式(即使用javah指令生成的头文件中那种格式),可以在Java代码中声明它对应的native方法,直接调 用。
比如,jni方法名为: jstringJNICALL Java_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方法。这种方法更加灵活。
jni函数的调用请参考我的另一篇博客http://blog.csdn.NET/u013403478/article/details/52068095,这里主要介绍第三方so库的配置、加载。
一、下图是我的项目JniDemo目录:
导入的两个第三方库是:libhello.so、libhello-jni.so
自己从源文件myhello.c编译生成的库是:libmyhello.so
java中调用native方法代码如下:
二、源文件的编写
在使用javah生成头文件后,要在源文件中使用include“xxxx.h”引入头文件。如果头文件不在jni根目录下,还要在Android.mk中使用
LOCAL_C_INCLUDES:=(相对于jni目录的)包含头文件的目录路径
来声明一下,否则报错找不到头文件。
然后实现自己声明的native方法,再在其中调用第三方库的函数。
具体代码如下:
三、Android.mk,Application.mk配置
Android.mk用来配置各个模块如何编译,如下:
Application.mk用来配置目标编译ABI(应用二进制接口),如arm64-v8a、armeabi、armeabi-v7a、mips、mips64、x86、x86_64。以armeabi-v7a为例,如下:
四、在终端使用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到了系统文件夹下,其他应用也可以调用
喽)。
demo项目源码下载地址:http://download.csdn.Net/detail/u013403478/9596669
转载请注明出处:http://blog.csdn.net/u013403478/article/details/52134870
简介
在Android日常的开发过程中有的项目需要引入第三方的库,有时候大家可能会在libs文件夹下看到
mips、armeabi、armeabi-v7a和x86这四个文件夹。那么这三个文件夹下面的包是干什么用的?
这三个包下面存放的用C编译的本地库文件(各类『.so』文件)。
mips、armeabi、armeabi-v7a和x86都表示CPU的类型。一般的手机或平板都是用arm的cpu。不同的cpu的特性不一样,armeabi就是针对普通的或旧的
arm v5 cpu,armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。
mips、armeabi、armeabi-v7a和x86到底是什么
mips:MIPS是世界上很流行的一种RISC处理器。MIPS的意思是“无内部互锁流水级的微处理器”(Microprocessor without interlocked piped stages),
其机制是尽量利用软件办法避免流水线中的数据相关问题。
armeabi:默认选项,将创建以基于 ARM* v5TE 的设备为目标的库。 具有这种目标的浮点运算使用软件浮点运算。 使用此 ABI (二进制接口)
创建的二进制代码将可以在所有 ARM* 设备上运行。所以armeabi通用性很强。但是速度慢
armeabi-v7a:创建支持基于 ARM* v7 的设备的库,并将使用硬件 FPU 指令。armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。
x86:支持基于硬件的浮点运算的 IA-32 指令集。x86是可以兼容armeabi平台运行的,无论是armeabi-v7a还是armeabi,同时带来的也是性能上的损耗,
另外需要指出的是,打包出的x86的so,总会比armeabi平台的体积更小。
总结
如果项目只包含了 armeabi,那么在所有Android设备都可以运行; 如果项目只包含了 armeabi-v7a,除armeabi架构的设备外都可以运行;
如果项目只包含了 x86,那么armeabi架构和armeabi-v7a的Android设备是无法运行的; 如果同时包含了 armeabi, armeabi-v7a和x86,
所有设备都可以运行,程序在运行的时候去加载不同平台对应的so,这是较为完美的一种解决方案,同时也会导致包变大。