在前面的文章中,咱们已经了解了Java函数和native函数的绑定过程,了解了Java和native的绑定方法,本文将介绍如何让一个库依赖其余库。java
首先介绍下动态库和静态库的概念。android
静态库c++
在开发过程当中,咱们经常会用到一些经常使用的公共函数,咱们能够将这些函数编到库中,在编写其余程序的时候一块儿整合到最终程序中,这种库就是静态库,在编译时连接,在Linux
下通常是.a
文件。git
动态库github
动态库在内部提供函数,让其余程序在运行时调用。在Linux
下通常经过dlopen
、dlsym
等函数动态寻找,在运行时连接,在Linux
下通常是.so
文件。bash
对于外部引入的静态库,咱们在编译时会将其一块儿打包到新生成的动态库中;
对于外部引入的动态库,咱们须要将它们一块儿打包到apk
中。app
这里以动态库为例,首先咱们编一个动态库:ide
新写一个CMakeLists.txt
函数
add_library(abi
SHARED
abi.cpp)
复制代码
在src/main/cpp/abi/abi.h
中定义以下函数gradle
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";
#else
return "unknown";
#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运行,结果以下
平时咱们通常会使用这种方式进行开发。
下面介绍下直接使用dlopen
、dlsym
进行调用的方法。
dlopen
和dlsym
进行调用dlfcn.h
中的函数介绍
// 打开动态库。
// 参数为:动态库路径,加载选项
// 返回值:动态库的句柄
void* dlopen(const char* __filename, int __flag);
// 关闭动态库
// 参数为:动态库的句柄
// 返回值:0表明成功,其余表明失败
int dlclose(void* __handle);
// 寻找动态库中的符号
// 参数为:动态库的句柄,符号名称
// 回传值,寻找到的对象
void* dlsym(void* __handle, const char* __symbol);
复制代码
经过dlopen
和dlsym
进行函数调用,流程说明:
代码以下:
extern "C" JNIEXPORT jstring
JNICALL
Java_com_wsy_jnidemo_MainActivity_getABIByDlopen(
JNIEnv *env,
jobject /* this */, jstring libPath) {
const char *cLibPath = env->GetStringUTFChars(libPath, JNI_FALSE);
void *handle = dlopen(cLibPath, RTLD_LAZY);
env->ReleaseStringUTFChars(libPath, cLibPath);
const char *abi = "unknown";
if (handle == NULL) {
LOGI("lib not found!");
} else {
LOGI("lib found!");
getAbiFunc getAbiFunction = (getAbiFunc) (dlsym(handle, "getAbi"));
if (getAbiFunction == NULL) {
LOGI("function not found!");
} else {
abi = getAbiFunction();
LOGI("getAbi by dlopen success, abi is : %s", abi);
}
dlclose(handle);
}
return env->NewStringUTF(abi);
}
复制代码
分别以armeabi-v7a
和arm64-v8a
运行,结果以下: