Android JNI介紹(六)- 依賴其餘庫

在前面的文章中,咱們已經瞭解了Java函數和native函數的綁定過程,瞭解了Java和native的綁定方法,本文將介紹如何讓一個庫依賴其餘庫。java

1、動態庫和靜態庫

首先介紹下動態庫和靜態庫的概念。android

  • 靜態庫c++

    在開發過程當中,咱們經常會用到一些經常使用的公共函數,咱們能夠將這些函數編到庫中,在編寫其餘程序的時候一塊兒整合到最終程序中,這種庫就是靜態庫,在編譯時連接,在Linux下通常是.a文件。git

  • 動態庫github

    動態庫在內部提供函數,讓其餘程序在運行時調用。在Linux下通常經過dlopendlsym等函數動態尋找,在運行時連接,在Linux下通常是.so文件。bash

對於外部引入的靜態庫,咱們在編譯時會將其一塊兒打包到新生成的動態庫中;
對於外部引入的動態庫,咱們須要將它們一塊兒打包到apk中。app

2、在Android Studio下引入外部庫

這裏以動態庫爲例,首先咱們編一個動態庫:ide

1. 編寫代碼

新寫一個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"
        }
    }
    ...
複製代碼

2. 編譯動態庫

而後點擊Android Studio右側的Gradle窗口中,指定moduleexternalNativeBuildRelease任務(也能夠gradlew執行命令)。

執行成功後,咱們就能在 build目錄下看到生成的動態庫文件

3. 將動態庫放到src/main/jniLibs目錄下

爲何是這個目錄,而不是其餘目錄?這是由於,src/main/jniLibsAndroid Studio工程的默認動態庫文件存放位置,若是將生成的庫放在其餘位置,打包apk的時候就不會把庫拿過來一塊兒打包,因而運行時就會缺乏libabi.so,從而致使crash,以下:

4. 在CMakeLists.txt中配置依賴的動態庫

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-v7aarm64-v8a等;

此時點擊externalNativeBuildReleaseRun窗口輸出以下,說明依賴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
複製代碼

5. 在工程中調用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());
}
複製代碼

modulebuild.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-v7aarm64-v8aABI運行,結果以下

平時咱們通常會使用這種方式進行開發。

下面介紹下直接使用dlopendlsym進行調用的方法。

6. 使用dlopendlsym進行調用

  • dlfcn.h中的函數介紹

    // 打開動態庫。
    // 參數爲:動態庫路徑,加載選項
    // 返回值:動態庫的句柄
    void* dlopen(const char* __filename, int __flag);
    // 關閉動態庫
    // 參數爲:動態庫的句柄
    // 返回值:0表明成功,其餘表明失敗
    int dlclose(void* __handle);
    // 尋找動態庫中的符號
    // 參數爲:動態庫的句柄,符號名稱
    // 回傳值,尋找到的對象
    void* dlsym(void* __handle, const char* __symbol);
    複製代碼
  • 經過dlopendlsym進行函數調用,流程說明:

    1. 打開動態庫文件
    2. 在動態庫中尋找函數
    3. 找到函數,則直接運行

    代碼以下:

    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-v7aarm64-v8a運行,結果以下:

Demo地址

github.com/wangshengya…

相關文章
相關標籤/搜索