Android Studio 中编译 JNI 代码

Android Studio 中编译 JNI 代码

经常需要实现一些 C,C++ 接口的功能模块,提供给 java 层调用。

那么就需要利用 JNI 写接口提供 java 的 native 调用。

必不可少的一步就是编译 JNI 代码。

需要的文件

  • (项目)
    • src
      • main
        • cpp
          • JNI 代码(依赖)
        • java
          • (接口类 class)
        • jniLibs
          • (ANDROID_ABI)
            • 依赖库文件
    • build.gradle
    • CMakeLists.txt
文件/目录 说明
src/main/cpp/ 用于存放JNI源码(C、C++等),也有人喜欢更名为 jni/
src/main/java/ JNI 接口的 java 调用
build.gradle 配置 CMake 编译等参数
CMakeLists.txt 用于设置 JNI 代码的编译方式

配置 build.gradle

android {
    ...
    defaultConfig {
        ...
        ndk {
            abiFilters "armeabi-v7a"
        }
    }

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

ndk.abiFilters 是配置编译的CPU架构,会根据架构编译出不同架构的 .so 文件。

cmake 是配置 CMake 的编译规则文件的路径,如果和 build.gradle 同级就不需要增加目录,否则按与 build.gradle 文件的路径自行配置。

添加 JNI 源码

个人习惯问题,喜欢按功能模块把 JNI 源码分类:

cpp/{功能}/include/:存放头文件

cpp/{功能}/src/:存放源代码

cpp/:存放 JNI 接口代码

这是个人习惯问题,大家可以无视。

一些 arm 架构的 so 库,建议放在 src/main/jniLibs/{ANDROID_ABI}/ 下,因为对于动态 .so 库,程序运行时需要依赖。

Android Studio 中编译 JNI 代码_第1张图片

添加 JNI 接口

在 JNI 中,需要自行根据添加 java native 的接口位置,添加 JNI 接口。

比如我想在:src/main/java/org/xxx/yyy/zzz/faceapi/FaceApiNative.java 中添加一个名为 JniJudgeHdr 的 native 接口。

    public native boolean JniJudgeHdr(byte[] bytes, int width, int height, int channels);

    static {
        System.loadLibrary("facehdr");
    }

那么我们需要增加的 JNI 接口为:

JNIEXPORT jboolean JNICALL
Java_org_xxx_yyy_zzz_faceapi_FaceApiNative_JniJudgeHdr(
        JNIEnv* env, jobject /* this */
        , jbyteArray jbArr_image
        , jint rows
        , jint cols
        , jint channels
        ) {
    ...
}

基本的文件框架为:

#include 
#include 

// #include "facehdr_jni.h" // 非必要
#include "judge_hdr/include/JudgeHdr.h" // 其它依赖的头文件

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jboolean JNICALL
Java_org_xxx_yyy_zzz_faceapi_FaceApiNative_JniJudgeHdr(
        JNIEnv* env, jobject /* this */
        , jbyteArray jbArr_image
        , jint rows
        , jint cols
        , jint channels
        ) {
    ...
}

#ifdef __cplusplus
}
#endif

同时如果 java 需要调用 JNI 接口,就需要设置加载对应的 JNI 库。(示例 System.loadLibrary 部分)

编译 CMake 规则

先上内容

cmake_minimum_required(VERSION 3.4.1)

# 编译后的 so 保存的位置
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

# 头文件路径
include_directories(
            ${CMAKE_SOURCE_DIR}/src/main/cpp
            ${CMAKE_SOURCE_DIR}/src/main/cpp/judge_hdr/include
        )

file(GLOB FACE_HDR_SRC
                        ${CMAKE_SOURCE_DIR}/src/main/cpp/facehdr_jni.cpp
                        )

add_library(
             facehdr
             SHARED
             ${FACE_HDR_SRC}
             )


# 添加依赖库
add_library(judgehdr SHARED IMPORTED )
set_target_properties(judgehdr PROPERTIES
        IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/src/main/${ANDROID_ABI}/libjudgehdr.so")
target_link_libraries(facehdr judgehdr)


find_library(
              log-lib

              log )

target_link_libraries(
                        facehdr
                       ${log-lib} )
字段 说明 参数
${CMAKE_SOURCE_DIR} 当前 CMake 规则文件的目录位置(即 CMakeLists.txt),
所以其它文件的路径可以根据这个位置来设置
include_directories 使用到的头文件路径,可以包含多个路径的头文件
file 可以将多个源码文件打包(这个包命名为FACE_HDR_SRC
add_library 将源码编译为对应的库(将 ${FACE_HDR_SRC}的源码编译为 libfacehdr.so 库文件) SHARED .so 动态库
STATIC .a 静态库
IMPORTED 引入外部库而不是编译生成库
set_target_properties 关联需要使用的其它依赖库
target_link_libraries 把其它库一起打包到到目标库中
find_library 关联其它库(主要是 Android Studio 提供的诸如 Log 的库)

接下来就可以运行了。

你可能感兴趣的:(Android,JNI)