在前面的文章中,咱們已經瞭解了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
運行,結果以下: