Android移植NDK子项目--以android-gif-drawable为例
2016-06-21 17:43
483 查看
前言:
一些非常优秀的开源项目引入起来都是比较麻烦的,麻烦到都可以写一篇博客来引导后来人少踩坑的程度。我在最近一次项目中想要做一个GIF的展示功能,光是引入著名的gifhub开源项目android-gif-drawable就花费了一番功夫,今天把我踩过的坑都记录一下,提醒一下准备使用这个开源库的盆友。
GIF三方库引入方式:
方式一:直接添加dependence
这种方式在Android studio中最简单的一种方式,只需打开build.gradle文件,加入这一句话即可。一般我引入三方库都直接使用这样的方法,简便快捷,仅需一行就完成了依赖的部署,然后clean一下项目就可以使用依赖库里的类和方法了。
从字面意思上来看,是在执行System.load()方法的时候没有找到对应的.so文件,然后我解压apk安装包,发现该so文件还在armeabi文件夹和x86文件夹下。
但是我的项目里却多出了多个CPU平台的so库,比如arm64_v8a,x86_64等,根据上面的报错异常可以看出,安卓系统找不到arm64下的.so文件,因为arm64_v8a文件夹全都是这个gif框架的so库,缺少本项目的so库。那么问题就显而易见了:
Android在加载时会根据当前cpu型号自动选择相应的so文件夹,然后加载里面的so库,如果我的apk里面有arm64_v8a文件夹,我使用的又是Android5.0 64位CPU,但文件夹里面又没有我要加载的so库,即使armeabi文件夹里有也不管用,就会出现这个异常。
这个问题的坑人之处在于如果apk包里没有arm64_v8a文件夹,系统会自动使用armeabi文件夹的so库顶上,也就是使用32位的方法兼容64位CPU,但是Android Studio在编译项目时将第三方库的64位so编译进了apk,而项目原本的so只有armeabi版本,所以导致加载so库失败。
要处理这个异常有两个途径,第一是使用ndk r11以上版本将本项目的so库编译出arm64_v8a的so,misp的so,x86_64等等so库,注意只有NDK r11以上版本才支持64位的编译,r10是在Android5.0发布之前发布的,安卓是在5.0之后才开始支持64位CPU,所以r10是不能编译arm和x86 64位版本的。
第一个途径有一个明显的弊端是so库使用的太多,会造成app的体积过大,而且如果原项目中依赖了其他的库,比如xUtils,它只提供了armeabi和x86的so,真是令人瞬间傻眼,所以就有了第二个解决途径。
方式二:使用aar包
第二类途径就是删掉一部分本项目没有而gif第三方库有的so,让两边的so库数量相同,这样就不会出现缺少某一CPU平台的问题了。该方案需要用到aar包。aar包可以看成是添加了相关图片资源文件的jar包,这个gif第三方库在github上也提供了它的arr包下载,如果你下载不成功的话,可以去github上下载该项目的源码,使用android studio打开,clean一下项目。然后到
:项目根目录\build\outputs\aar文件夹下,取得android-gif-drawable-master-debug.aar,打开后如下图:
现在只需要在这个aar文件中将armeabi,x86以外的文件夹删掉,就可以避免在编译时将不需要的so文件打入apk了。
那么如何将arr导入自己的项目作为依赖,网上有很多方法,下面说一种:
这里演示的aar文件为: ”genius.aar“
第一步:拷贝到:libs目录
第二步:build.gradle 配置文件中更改为
摘自:http://www.tuicool.com/articles/V7reai
这样就可以请可以轻松的将一个外部NDK项目导入到自己的项目中使用了。
但是如果我想把外部NDK项目的c/c++源码一并导入进来并进行修改,过程就比较复杂了
方式三:将第三方库的源码全部导入自己的项目
将外部NDK项目完全导入自己的项目步骤:
1、去github上下载项目zip源码
2、解压zip,并使用Android Studio打开该项目,就像打开普通项目一样,点击file--project stucture--设置NDK的路径,如下
注意NDK使用r11以上版本,因为r10版本没有Android 23的jar包
3、使用AndroidStudio 把这个项目clean一下
4、将第三方项目拷贝到自己项目的libraries文件夹下
5、使用Android Studio打开自己的项目,在build.gradle中添加如下内容(android-gif-drawable 是libraries 里面的文件夹的名字)
如果导入成功,那么就可以看下面的部分,如果导入不成功,一般是缺少build-tools等sdk的依赖,一般翻墙下载就能解决,或者在build.gradle修改min sdk version , build tools version 等.
8、修改子项目的so库编译选项
进入gif子项目的jni目录,如下,修改Application.mk中的APP_ABI:all为APP_ABI := armeabi x86,这样就可以只编译armeabi x86两个版本的so库,就可以和项目里其他的so库匹配起来了。
9、去掉子项目编译前的单元测试
我在使用Android Studio编译整个项目时,并由执行gif的单元测试,但是我在使用命令行gradle build命令时,会首先执行gif子项目的单元测试,一共21个测试项目,如果失败任何一个编译过程就会终止。所以需要关掉单元测试,方法是修改build.gradle文件并删除相关测试文件,下面gif依赖库的build.gradle注释掉的内容即为和单元测试有关的内容:
删除相关单元测试的java文件,不然会报编译错误,如下:
现在子项目已经完全移植到自己的项目中,可以打包apk了。
一些非常优秀的开源项目引入起来都是比较麻烦的,麻烦到都可以写一篇博客来引导后来人少踩坑的程度。我在最近一次项目中想要做一个GIF的展示功能,光是引入著名的gifhub开源项目android-gif-drawable就花费了一番功夫,今天把我踩过的坑都记录一下,提醒一下准备使用这个开源库的盆友。
GIF三方库引入方式:
方式一:直接添加dependence
这种方式在Android studio中最简单的一种方式,只需打开build.gradle文件,加入这一句话即可。一般我引入三方库都直接使用这样的方法,简便快捷,仅需一行就完成了依赖的部署,然后clean一下项目就可以使用依赖库里的类和方法了。
dependencies { compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.+' }但是我这样引入以后再运行阶段出现一个crash 异常,如下:
AndroidRuntime: FATAL EXCEPTION: DBExecutor #1 Process: com.qraved.app, PID: 15616 java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/framework/com.google.android.maps.jar", zip file "/data/app/com.qraved.app-2/base.apk"],nativeLibraryDirectories=[/data/app/com.qraved.app-2/lib/arm64, /vendor/lib64, /system/lib64]]] couldn't find "libQraved.so" at java.lang.Runtime.loadLibrary(Runtime.java:366) at java.lang.System.loadLibrary(System.java:988) at com.imaginato.qravedconsumer.utils.JConstantUtils.<clinit>(Unknown Source) at com.imaginato.qravedconsumer.handler.bk.<init>(Unknown Source) at com.imaginato.qravedconsumer.handler.bj.f(Unknown Source) at com.imaginato.qravedconsumer.handler.aq.a(Unknown Source) at com.imaginato.qravedconsumer.handler.aq.a(Unknown Source) at com.imaginato.qravedconsumer.handler.io.a(Unknown Source) at com.imaginato.qravedconsumer.handler.io.a(Unknown Source) at com.imaginato.qravedconsumer.handler.hd.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818)
从字面意思上来看,是在执行System.load()方法的时候没有找到对应的.so文件,然后我解压apk安装包,发现该so文件还在armeabi文件夹和x86文件夹下。
但是我的项目里却多出了多个CPU平台的so库,比如arm64_v8a,x86_64等,根据上面的报错异常可以看出,安卓系统找不到arm64下的.so文件,因为arm64_v8a文件夹全都是这个gif框架的so库,缺少本项目的so库。那么问题就显而易见了:
Android在加载时会根据当前cpu型号自动选择相应的so文件夹,然后加载里面的so库,如果我的apk里面有arm64_v8a文件夹,我使用的又是Android5.0 64位CPU,但文件夹里面又没有我要加载的so库,即使armeabi文件夹里有也不管用,就会出现这个异常。
这个问题的坑人之处在于如果apk包里没有arm64_v8a文件夹,系统会自动使用armeabi文件夹的so库顶上,也就是使用32位的方法兼容64位CPU,但是Android Studio在编译项目时将第三方库的64位so编译进了apk,而项目原本的so只有armeabi版本,所以导致加载so库失败。
要处理这个异常有两个途径,第一是使用ndk r11以上版本将本项目的so库编译出arm64_v8a的so,misp的so,x86_64等等so库,注意只有NDK r11以上版本才支持64位的编译,r10是在Android5.0发布之前发布的,安卓是在5.0之后才开始支持64位CPU,所以r10是不能编译arm和x86 64位版本的。
第一个途径有一个明显的弊端是so库使用的太多,会造成app的体积过大,而且如果原项目中依赖了其他的库,比如xUtils,它只提供了armeabi和x86的so,真是令人瞬间傻眼,所以就有了第二个解决途径。
方式二:使用aar包
第二类途径就是删掉一部分本项目没有而gif第三方库有的so,让两边的so库数量相同,这样就不会出现缺少某一CPU平台的问题了。该方案需要用到aar包。aar包可以看成是添加了相关图片资源文件的jar包,这个gif第三方库在github上也提供了它的arr包下载,如果你下载不成功的话,可以去github上下载该项目的源码,使用android studio打开,clean一下项目。然后到
:项目根目录\build\outputs\aar文件夹下,取得android-gif-drawable-master-debug.aar,打开后如下图:
现在只需要在这个aar文件中将armeabi,x86以外的文件夹删掉,就可以避免在编译时将不需要的so文件打入apk了。
那么如何将arr导入自己的项目作为依赖,网上有很多方法,下面说一种:
这里演示的aar文件为: ”genius.aar“
第一步:拷贝到:libs目录
第二步:build.gradle 配置文件中更改为
repositories { flatDir { dirs 'libs' } } dependencies { compile(name:'genius', ext:'aar') }
摘自:http://www.tuicool.com/articles/V7reai
这样就可以请可以轻松的将一个外部NDK项目导入到自己的项目中使用了。
但是如果我想把外部NDK项目的c/c++源码一并导入进来并进行修改,过程就比较复杂了
方式三:将第三方库的源码全部导入自己的项目
将外部NDK项目完全导入自己的项目步骤:
1、去github上下载项目zip源码
2、解压zip,并使用Android Studio打开该项目,就像打开普通项目一样,点击file--project stucture--设置NDK的路径,如下
注意NDK使用r11以上版本,因为r10版本没有Android 23的jar包
3、使用AndroidStudio 把这个项目clean一下
4、将第三方项目拷贝到自己项目的libraries文件夹下
5、使用Android Studio打开自己的项目,在build.gradle中添加如下内容(android-gif-drawable 是libraries 里面的文件夹的名字)
dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':libraries:android-gif-drawable') }6、在settings.gradle中添加一行,并像刚才子项目一样添加NDK的依赖
include ':libraries:android-gif-drawable'7、然后clean一下当前项目。然后进入点击file--project structure查看外部项目是否已经导入进了项目目录,如下
如果导入成功,那么就可以看下面的部分,如果导入不成功,一般是缺少build-tools等sdk的依赖,一般翻墙下载就能解决,或者在build.gradle修改min sdk version , build tools version 等.
8、修改子项目的so库编译选项
进入gif子项目的jni目录,如下,修改Application.mk中的APP_ABI:all为APP_ABI := armeabi x86,这样就可以只编译armeabi x86两个版本的so库,就可以和项目里其他的so库匹配起来了。
9、去掉子项目编译前的单元测试
我在使用Android Studio编译整个项目时,并由执行gif的单元测试,但是我在使用命令行gradle build命令时,会首先执行gif子项目的单元测试,一共21个测试项目,如果失败任何一个编译过程就会终止。所以需要关掉单元测试,方法是修改build.gradle文件并删除相关测试文件,下面gif依赖库的build.gradle注释掉的内容即为和单元测试有关的内容:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.0' //classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'//不进行代码覆盖率的测试 } } apply plugin: 'com.android.library' //apply plugin: 'jacoco-android'//不进行代码覆盖率的测试 apply from: 'gradle-mvn-push.gradle' apply from: 'ndk.gradle' repositories { jcenter() } project.version = VERSION_NAME project.group = GROUP android { compileSdkVersion 23 buildToolsVersion '23.0.2'//如果编译器说build tools version本机sdk没有,就选择一个本机sdk里有的版本 compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } defaultConfig { versionName project.version minSdkVersion 8 targetSdkVersion 23 consumerProguardFiles 'consumer-proguard-rules.pro' } } dependencies { provided 'com.android.support:support-annotations:23.3.0'//如果这里报错请修改版本号并翻墙,下面的也是 // testCompile 'junit:junit:4.12'//在编译时会进行单元测试,大约21个,如果有任何一个测试没有通过就会终止编译,所以将所有单元测试去掉 // testCompile 'org.mockito:mockito-core:1.10.19' // testCompile 'org.robolectric:robolectric:3.0' // testCompile 'org.assertj:assertj-core:1.7.1' // testCompile 'net.jodah:concurrentunit:0.4.2' } //jacocoAndroidUnitTestReport {//不进行代码覆盖率的测试 // html.enabled true // xml.enabled true //}
删除相关单元测试的java文件,不然会报编译错误,如下:
现在子项目已经完全移植到自己的项目中,可以打包apk了。
相关文章推荐
- Android动画
- Android--->中的一些设计模式
- Android传递事件解析
- android 获取栈顶activty的方法总结(兼容API 5.0)
- 关于Android适配华为等带有底部虚拟按键的解决方案
- Android studio gradle配置
- android 获取栈顶activty的方法总结(兼容API 5.0)
- android 获取栈顶activty的方法总结(兼容API 5.0)
- 获取当前运行平台
- android ndk : error pluginprotocolstatic: local_src_files 错误
- Android获取view尺寸的三种方法
- Android Fragment 构造函数
- 在Android Studio中设置或修改默认的Android SDK路径。
- Android 获取ListView中的item控件(mListView.getChildAt(int pos))
- Android 解决ViewPager设置高度为wrap_content无效的方法 MyViewPager
- 安装Genymotion报Unable to load VirtualBox engine错误解决办法
- Android基础复习
- Android 下拉刷新框架实现
- Android.mk 文件语法详解
- Android中Handler引起的内存泄露