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…

相关文章
相关标签/搜索