在前面的文章中,我们已经了解了Java函数和native函数的绑定过程,了解了Java和native的绑定方法,本文将介绍如何让一个库依赖其他库。
首先介绍下动态库和静态库的概念。
在开发过程中,我们常常会用到一些常用的公共函数,我们可以将这些函数编到库中,在编写其他程序的时候一起整合到最终程序中,这种库就是静态库,在 编译时 链接,在 Linux 下一般是 .a 文件。
动态库在内部提供函数,让其他程序在运行时调用。在 Linux 下一般通过 dlopen 、 dlsym 等函数动态寻找,在 运行时 链接,在 Linux 下一般是 .so 文件。
对于外部引入的静态库,我们在编译时会将其一起打包到新生成的动态库中;
对于外部引入的动态库,我们需要将它们一起打包到 apk 中。
这里以动态库为例,首先我们编一个动态库:
新写一个 CMakeLists.txt
add_library(abi
SHARED
abi.cpp)
复制代码
在 src/main/cpp/abi/abi.h 中定义如下函数
extern "C" const char *getAbi(); 复制代码
src/main/cpp/abi/abi.cpp 内容如下,不同 ABI 的动态库会回传不同的结果
#include "abi.h"
const char *getAbi() {
#ifdef __arm__
return "arm32";
#elif __aarch64__
return "arm64";
#elif __i386__
return "x86";
#elif __x86_64__
return "x86_64";
#endif
}
复制代码
在 build.gradle 中重新指定 CMakeLists.txt 的路径
...
externalNativeBuild {
cmake {
// path "CMakeLists.txt"
path "src/main/cpp/abi/CMakeLists.txt"
}
}
...
复制代码
然后点击 Android Studio 右侧的 Gradle 窗口中,指定 module 的 externalNativeBuildRelease 任务(也可以 gradlew 执行命令)。
build
目录下看到生成的动态库文件
src/main/jniLibs 目录下
为什么是这个目录,而不是其他目录?这是因为, src/main/jniLibs 是 Android Studio 工程的默认动态库文件存放位置,如果将生成的库放在其他位置,打包apk的时候就不会把库拿过来一起打包,于是运行时就会缺少 libabi.so ,从而导致crash,如下:
在 build.gradle 中切换回原来的 CMakeLists.txt
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
// path "src/main/cpp/abi/CMakeLists.txt"
}
}
...
复制代码
CMakeLists.txt 内容如下,添加依赖
cmake_minimum_required(VERSION 3.4.1)
add_library(
native-lib
SHARED
src/main/cpp/native-lib.cpp)
find_library(
log-lib
log)
set(abi-lib ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libabi.so)
message("abi-lib is ${abi-lib}")
target_link_libraries(
native-lib
${abi-lib}
${log-lib})
复制代码
其中,
message 函数用于输出信息到控制台;
set 函数用于创建变量,用法为 set(variable value) ;
CMAKE_SOURCE_DIR 表示当前 CMakeLists.txt 文件所在的目录;
CMAKE_ANDROID_ARCH_ABI 表示当前编译的 ABI ,如 armeabi-v7a 、 arm64-v8a 等;
此时点击 externalNativeBuildRelease , Run 窗口输出如下,说明依赖 libabi.so 成功
release|armeabi-v7a :abi-lib is D:/android/android-projects/git/JNIDemo/app/src/main/jniLibs/armeabi-v7a/libabi.so release|armeabi-v7a :-- Configuring done release|armeabi-v7a :-- Generating done release|armeabi-v7a :-- Build files have been written to: D:/android/android-projects/git/JNIDemo/app/.externalNativeBuild/cmake/release/armeabi-v7a release|arm64-v8a :abi-lib is D:/android/android-projects/git/JNIDemo/app/src/main/jniLibs/arm64-v8a/libabi.so release|arm64-v8a :-- Configuring done release|arm64-v8a :-- Generating done release|arm64-v8a :-- Build files have been written to: D:/android/android-projects/git/JNIDemo/app/.externalNativeBuild/cmake/release/arm64-v8a release|x86 :abi-lib is D:/android/android-projects/git/JNIDemo/app/src/main/jniLibs/x86/libabi.so release|x86 :-- Configuring done release|x86 :-- Generating done release|x86 :-- Build files have been written to: D:/android/android-projects/git/JNIDemo/app/.externalNativeBuild/cmake/release/x86 release|x86_64 :abi-lib is D:/android/android-projects/git/JNIDemo/app/src/main/jniLibs/x86_64/libabi.so release|x86_64 :-- Configuring done release|x86_64 :-- Generating done release|x86_64 :-- Build files have been written to: D:/android/android-projects/git/JNIDemo/app/.externalNativeBuild/cmake/release/x86_64 复制代码
libabi.so 中的函数 在Java代码中定义native函数并调用
public native String getABI(); ...... Log.i(TAG, "onCreate: getABI = " + getABI()); ...... 复制代码
native实现
...
#include "abi/abi.h"
...
extern "C" JNIEXPORT jstring
JNICALL
Java_com_wsy_jnidemo_MainActivity_getABI(
JNIEnv *env,
jobject /* this */) {
return env->NewStringUTF(getAbi());
}
复制代码
在 module 的 build.gradle 中进行配置,以指定 ABI 运行
android{
...
defaultConfig{
...
ndk{
abiFilters "armeabi-v7a" // 指定以armeabi-v7a运行
// abiFilters "arm64-v8a","armeabi-v7a" // 以arm64-v8a、armeabi-v7a中,目标设备支持的最优ABI运行
// abiFilters "arm64-v8a" // 指定以arm64-v8a运行
}
...
}
...
}
复制代码
前后以 armeabi-v7a 、 arm64-v8a ABI运行,结果如下