💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 1. 简介 ## 1.1 功能 JNI是Java Native Interface的缩写,在实际中主要也是为了完成C/C++和Java程序的互相调用。因为C/C++存在更久,且对于浮点数的计算更快,且Android底层驱动大多是C/C++来完成的。所以有些时候需要使用C/C++代码,比如驱动开发、音频开发等。 ## 1.2 Android中的JNI 在Android中JUC编程中,尤其是在锁的底层,我们都可以看到native修饰的方法,这个也就是本地方法。在Android中使用NDK这个工具包来完成JNI的开发。JNI可以看做是规定的协议,而NDK是谷歌提供的实践的工具。 ## 1.3 NDK NDK是一套工具原生开发套件,使你能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库,你可使用这些平台库管理原生 Activity 和访问物理设备组件,例如传感器和轻触输入。那么什么时候可以考虑使用NDK? * 提高应用的执行效率。将浮点数计算过多的逻辑使用C/C++语言开发。 * 代码的保护。C/C++库反编译难度较大。 * 可以方便地使用C/C++代码编写的开源库,比如OpenCV做图像识别。 * 便于移植。方便在其他的嵌入式平台上使用。 # 2. NDK 环境配置 ## 2.1 下载地址 可以直接从官网中选择所需要的版本,地址:[NDK 修订历史记录  |  Android NDK  |  Android Developers](https://developer.android.com/ndk/downloads/revision_history) ## 2.2 AndroidStudio中配置 这里可以直接在AndroidStudio中进行配置,直接进行下载: ![](https://img.kancloud.cn/00/ff/00ffa86392086fe4305e07eecfa73cf2_895x361.png) 选择NDK,应用即可下载。然后可以在本地的AndroidSDK环境中找到刚刚下载的NDK: ![](https://img.kancloud.cn/f9/92/f99273e615a4a5e48d392ee9a850522a_1052x669.png) 然后配置一下环境变量,配置后就可以使用cmd进行检测: ![](https://img.kancloud.cn/4f/20/4f20a8b745b20d8c0b40adf58829eca0_1425x239.png) 然后在local.properties文件中配置: ![](https://img.kancloud.cn/63/95/63959a940b130023638481346f719e13_1153x361.png) 配置好后可以再次检查一次: ![](https://img.kancloud.cn/ee/ad/eead4a6c5588dc99c61c775305b6ab19_1438x580.png) # 3. JNI案例 首先来体验一下,直接使用Android Studio直接创建,即: ![](https://img.kancloud.cn/76/75/7675fd9af9bdf70a4600b1b51ce7b1f5_1110x804.png) 然后选择C++版本为11: ![](https://img.kancloud.cn/8d/df/8ddf8d46191a1aa8f664e3e0c33c9491_873x436.png) 首先来看下这个项目和普通的项目有什么不同: ## 3.1 项目结构 ![](https://img.kancloud.cn/42/13/421369f8d418ef176eb3dcc4f3ee4e2e_445x594.png) 在项目目录中的main文件夹下多了cpp文件夹,在该文件夹下使用了CMake配置和Cpp文件。分别看下这个两个文件: ### 3.1.1 CMakeLists.txt ~~~ # 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.10.2) # Declares and names the project. project("jnitest") # 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. add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). native-lib.cpp ) # 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. log-lib # Specifies the name of the NDK library that # you want CMake to locate. 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. native-lib # Links the target library to the log library # included in the NDK. ${log-lib} ) ~~~ 从注释中我们知道,关于如何使用CMake给了一个文档地址:https://d.android.com/studio/projects/add-native-code.html 在这个配置文件中做了这么几件事: * 指定最小CMake版本和项目名称; * 添加类库,也就是这里的第二个文件,native-lib.cpp; * 指定查找路径和链接库的名字; ### 3.1.2 MainActivity.java ~~~ public class MainActivity extends AppCompatActivity { // 加载 static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 调用 TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } public native String stringFromJNI(); } ~~~ 起始也就是定义供Java代码调用的接口,这里即为native修饰的stringFromJNI方法。 然后我们编译一下项目,可以看见生成的SO文件: ![](https://img.kancloud.cn/ac/48/ac48677f0916a8a4b76d0cc7d6f6667d_482x374.png) ### 3.1.3 native-lib.cpp 最后来看下这个文件: ~~~ #include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_weizu_jnitest_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } ~~~ 方法的命名规则为Java\_全类名\_本地方法名。至于类型这里为JNI所规定的,后面在继续学习。 ### 3.1.4 build.gradle 在该模块的build.gradle文件中: ~~~ android { ... defaultConfig { ... externalNativeBuild { cmake { cppFlags "-std=c++11" } } } externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.10.2" } } } ~~~ 也配置了对应的C++版本和对应的Cmake配置和版本。 然后运行一下项目,可以看见确实成功了: ![](https://img.kancloud.cn/84/26/842694ba3d870b7ca887d9ffde63b17f_277x114.png) 从这个流程来看这里所使用的方式和我们在第二步中所配置的略微不相关。那么接下来就使用cmake的方式。接下来尝试将其按照上面的规则进行迁移到任意一个Android Studio项目中。 # 4. 项目配置 拷贝上面的CMakeLists.txt以及lib.cpp文件到新的项目,然后修改一下native-lib.cpp文件: ~~~ #include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_weizu_jnitest_JNITest_helloJni( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } ~~~ 然后在新工程中创建对应的包名和方法: ~~~ package com.weizu.jnitest; public class JNITest { // 加载 static { System.loadLibrary("native-lib"); } // Java_com_weizu_jnitest_MainActivity_helloJni public native String helloJni(); } ~~~ 最后拷贝一下build.gradle中的配置内容,指定C++版本和CMakeLists.txt文件路径。然后就可以调用了: ~~~ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.e("TAG", "onCreate: ${JNITest().helloJni()}" ) } } ~~~ 结果: ![](https://img.kancloud.cn/c8/a9/c8a90774d55dd6087e52dd111fc04f7d_1048x94.png)