1.abiFilters 是做什么用的?
我们在项目的 gradle 中经常会看到这样的配置:
- defaultConfig {
- ...
- ndk {abiFilters "armeabi-v7a", "x86"}
- }
那为什么要这样配置呢, 一起来看下:
如果我们在项目中引入了某个 SDK, 这个 SDK 中支持 armeabi,armeabi-v7a,arm64-v8a,x86,x86_64 五种 ABI, 但是我们的项目中只支持 armeabi-v7a 和 x86 两种 ABI, 这样打包 APK 后就会生成五种 ABI 目录, 其中 SDK 中的 so 库是正常的, 但是我们项目中的 so 库就会只存在于 armeabi-v7a 和 x86 两个目录中, 应用安装在 armeabi-v7a 或 x86 架构的设备上是没有问题的, 但是安装在其他架构的设备上 (比如 arm64-v8a ) 就会出现下面的异常, 找不到对应的 so 库:
- java.lang.UnsatisfiedLinkError: Couldn't load native-lib from loader dalvik.system.PathClassLoader[DexPathList[[zip file"/data/app/com.yl.ndkdemo-1.apk"]
- nativeLibraryDirectories=[/data/app-lib/com.yl.ndkdemo-1, /vendor/lib, /system/lib, /system/lib/arm]]]: findLibrary returned null
那该怎么解决呢? 有人会说可以把 SDK 中多出来的三种 ABI 删掉, 嗯, 也是个方法, 但是如果是远程库引用就没办法了, 这时就轮到 abiFilters 出马了, 限制安装包 ABI 的架构, 不管 SDK 或者项目中有多少种 ABI 架构, 最终打包进 APK 的 so 库都以 abiFilters 中规定的为准.
也就是说, 如果我们的项目中有五种 ABI 库, 但是 abiFilters 中值设置了两种 ABI 支持, 那么我们最终打包的 APK 中只会包含这两种 ABI 库.
2.ABIs [armeabi] are not supported for platform.
最新的 NDK r17 版本, 已经去掉了 armeabi,mips,mips64 的 ABI 支持.
如果在编译的过程中遇到下面的报错, 不要方, 去掉 gradle 中配置的 armeabi 选项就可以了:
ABIs [armeabi] are not supported for platform. Supported ABIs are [armeabi-v7a, arm64-v8a, x86, x86_64].
去掉 abiFilters 中的 armeabi 选项, armeabi-v7a 会兼容 armeabi:
- defaultConfig {
- ...
- ndk {
- abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
- }
- }
3. 如何 debug C/C++ 源码?
首先安装 LLDB 调试工具(File> Settings> Appearance & Behavior> System Settings> Android SDK> SDK Tools):
LLDB
LLDB 安装完成后就可以 debug 源码了, 和 debug java 代码的流程基本一致, 看下效果:
debug 源码
在 debug 的过程中可能会遇到一些诡异的问题, 比如下面这行报错:
Attention! No symbol directories found - please check your native debug configuration.
大概意思是 symbol 路径找不到, 请检查一遍你的 debug 配置, 如果你的项目中 JNI 代码在 Library 中, 这个错误发生的几率会比较大.
在网上查到这样一种方法, 打开 Run> Edit Configurations> Debugger> Symbol Directories 选项, 在里面配置一下 Library 的路径, 如下图所示:
配置 Symbol Directories
debug 运行一下, 正常了, 但是第二次再 debug 的时候继续报上面的错, 继续查啊查, 看到这样一条解决方案, 把 local.properties 文件中 ndk 的路径改成错的, 编译一下(肯定报错), 再改回原来的就正常了, 什么? 还有这样的操作, 抱着怀疑的态度试了下, 居然可以了... 可以了...
local.properties
这个问题随机出现, 可能是 AS 抽风了, 遇到了可以这样试试.
4. 如何使用编译好的 so 库?
有读者在评论中问我这个问题, 在这里总结下, 写一个简单的 Demo, 返回一个字符串给 Java 层, 方法名是通过 Java_包名_类名_方法名 的方式命名的, 其中包名和类名就是 Java 层调用类的包名和类名, 这是一种规定的命名方式, 不可以修改, 看下代码:
- #include <jni.h>
- #include <string>
- extern "C"
- JNIEXPORT jstring
- JNICALL
- Java_com_yl_ndklibrary_NDKLibrary_stringFromJNI(
- JNIEnv *env,
- jobject /* this */) {
- std::string hello = "Hello from C++";
- return env->NewStringUTF(hello.c_str());
- }
Java 层:
- package com.yl.ndklibrary;
- public class NDKLibrary {
- static {
- System.loadLibrary("native-lib");
- }
- public static native String stringFromJNI();
- }
编译好 so 库后, 如果想要提供给其他项目使用, 则项目中调用 so 库的类必须和 so 库编译时的类 (例如: NDKLibrary) 类名和包名完全一致, 否则就会报下面的错误:
java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.yl.ndkdemo.NDKLibrary.stringFromJNI()
通常我们会把 so 库和调用类打包成一个 Library 提供给第三方调用者, 调用者拿到 Library 后, 直接调用其中的方法就可以了, 不必关心类名与包名, 这也是开源项目中的常见做法.
5. 写在最后
文章中如有遗漏, 错误的地方, 可以给我留言指正, 多谢!
本文中用到的源码已上传至 GitHub, 欢迎 Fork, 觉得还不错就 Start 一下吧!
GitHub 传送门 https://github.com/alidili/Demos/tree/master/NDKLibraryDemo
来源: http://www.jianshu.com/p/fc736f881220