Android 添加JNI 开发能力

JNI 与 NDK 区别
JNI:JNI是一套编程接口,用来实现Java代码与本地的C/C++代码进行交互;
NDK: NDK是Google开发的一套开发和编译工具集,可以生成动态链接库,主要用于Android的JNI开发;

使用cMake或者ndk-bundle 编译(推荐使用Cmake)

使用cMake

环境需求
要进行jni开发,AS需要以下环境:
在AndroidStudio下载以下插件


image.png

新建支持c的项目


image.png

新建完后在编译即可

有时候,我们的项目已经在进行中或者维护中了,突然需要使用jni调用怎么办呢?AS已经提供了相对便捷的方法。首先在要使用jni调用的工程模块下新建一个CMakeLists.txt:
本文主要讲解在现有老项目如何添加JNI 支持,
1、配置ndk路径


image.png

2、在app目录下新建cMakeList文件(不一定要在app目录下,只要是在build文件里面配置的路径一致即可)


image.png
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

# 配置so库信息
add_library( # Sets the name of the library.
        # 生成的so库名称,此处生成的so文件名称是libnative-lib.so
        hello

        # Sets the library as a shared library.
        # STATIC:静态库,是目标文件的归档文件,在链接其它目标的时候使用
        # SHARED:动态库,会被动态链接,在运行时被加载
        # MODULE:模块库,是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接
        SHARED

        # Provides a relative path to your source file(s).
        # 资源文件,可以多个,
        # 资源路径是相对路径,相对于本CMakeLists.txt所在目录
        # src/main/jni/test.cpp
        src/main/jni/hello.c)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

# 从系统查找依赖库
find_library( # Sets the name of the path variable.
        # android系统每个类型的库会存放一个特定的位置,而log库存放在log-lib中
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        # android系统在c环境下打log到logcat的库
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

# 配置库的链接(依赖关系)
target_link_libraries( # Specifies the target library.

        # 目标库
        hello

        # Links the target library to the log library
        # included in the NDK.
        # 依赖于
        ${log-lib})
image.png

CMakeLists.txt具体配置上面已经说过了,这个地方是去掉了注释了的。需要注意的是,如果我们不在c源代码文件中输出日志到logcat,那么我们是不需要依赖log库的,也就是说find_library、target_link_libraries不是必须的。

将 find_library()命令添加到您的 CMake 构建脚本中以定位 NDK 库,并将其路径存储为一个变量。您可以使用此变量在构建脚本的其他部分引用 NDK 库。以下示例可以定位 Android 特定的日志支持库并将其路径存储在 log-lib 中:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

为了确保您的原生库可以在 log 库中调用函数,您需要使用 CMake 构建脚本中的 target_link_libraries() 命令关联库:

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

NDK 还以源代码的形式包含一些库,您在构建和关联到您的原生库时需要使用这些代码。您可以使用 CMake 构建脚本中的 add_library() 命令,将源代码编译到原生库中。要提供本地 NDK 库的路径,您可以使用 ANDROID_NDK 路径变量,Android Studio 会自动为您定义此变量。

接着配置模块支持jni调用,对项目模块右键:


image.png

image.png
image.png

在JniTest里面定义好暴露出去的方法,新建hello.c文件在c里面实现这里个方法

public class JniTest {
    // Used to load the 'native-lib' library on application startup.
    // 加载库
    static {
        System.loadLibrary("hello");
    }
    // native申明 在c里面实现
    public native String stringTest();

    public native String stringHello();

#include <jni.h>

JNIEXPORT jstring JNICALL
Java_com_jni_JniTest_stringHello(JNIEnv *env, jobject instance) {
    // TODO
    return (*env)->NewStringUTF(env, "helloJNi");
}


JNIEXPORT jstring JNICALL
Java_com_jni_JniTest_stringTest(JNIEnv *env, jobject instance) {

    // TODO

    return (*env)->NewStringUTF(env, "hello_new_eeeeetest");
}

运行在app里面调用new JniTest().stringTest() 即可拿到在c里面给的结果


image.png

然后Make Project成功后,会在如下目录生成.so文件.此时.so库生成成功,可随时调用了!
编译的包里面lib里面已经包含了so

image.png

cmake里面也包含了 当前对应环境的so 文件(这些so文件便可以给第三方使用了)


image.png

总之,JNI入门难度大大降低,AS在这方面还有很多适用的功能提供,例如debug、在c代码中输入日志到logcat等,只需简单操作就可完成,非常人性。
参考文章
使用ndk-build编译 请参考这篇文章
AndroidStudio现有项目添加NDK支持(CMake编译)

使用ndk-build编译 生成头文件方式
使用javac编译生成class

image.png

函数返回值
以下是一个JNI函数:
JNIEXPORT jstring JNICALL Java_com_mm_NativeHelper_sayHello(JNIEnv *, jclass, jstring);
在JNIEXPORT和JNICALL宏中间的jstring,表示函数的返回值类型,对应Java的String类型。

使用javah生成h文件
在不熟悉的情况下,可以通过javah生成JNI函数头文件;
当熟悉了JNI的native函数命名规则之后,直接按照函数命名规则编写相应的函数原型和实现即可。

image.png

githubDemo

推荐阅读更多精彩内容