您的位置:首页 > 移动开发 > Android开发

Android移植NDK子项目--以android-gif-drawable为例

2016-06-21 17:43 483 查看
前言:

一些非常优秀的开源项目引入起来都是比较麻烦的,麻烦到都可以写一篇博客来引导后来人少踩坑的程度。我在最近一次项目中想要做一个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了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: