在前面的文章中,我们已经了解了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运行,结果如下