JNI:全名 Java Native Interface,是Java本地接口,属于Java体系
javac编译java源文件(生成.class文件)javah生成头文件(生成.h头文件).so库文件java执行Java程序,最终实现java调用native本地代码(借助.so文件)NDK:Native Development Kit,本地开发工具包,属于Android体系
快速开发 C/C++ 动态库,自动将.so和应用一起打包成apk
ndk-build编译产生.so库文件在AndroidStudio中查看ProjectStructure中的 sdk location,发现 android ndk location 无法手动选择,通过local.properties文件添加 ndk 路径,再去查看 sdk location 发现 ndk location 已经有显示
sdk.dir=D\:\\Android\\SDK
// 添加这样的配置
ndk.dir=D\:\\Android\\SDK\\ndk-bundle
进入gradle.properties文件下,添加对旧版本 ndk 的支持
// 对旧版本的 ndk 支持
android.useDeprecatedNdk=true
为本地库做一些配置,包括版本、名称、日志库关联
# 设置用来构建本地库的CMake最低版本
cmake_minimum_required(VERSION 3.18.1)
# 设置本地库名字、分享类型、本地库的路径
add_library(CPP SHARED src/main/cpp/native-lib.cpp)
# 定义一个路径变量,log-lib这个变量中的值就是Android中Log库的路径
find_library(log-lib log)
# 将本地库与日志库关联,这样就能在本地库中使用log库方法
target_link_libraries(CPP ${log-lib})
通过 AndroidStudio 新建一个项目(CPlusPlusApp),选择最底部的 Native C++,一直 next 等待编译完成后,运行项目
当然手动创建也是可以的
在 src\main 目录下新建 cpp目录
在 src\main\cpp 下新建 CMakeLists.txt,按照上面说的格式新建一份
在module下的 build.gradle 配置 externalNativeBuild
android{
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.18.1'
}
}
}
在应用中初始化库文件,并创建 native 方法,选择合适的地方调用该方法运行即可
compaion object{
init{
// CMakeLists.txt中声明的库名称
Syste.loadLibrary(libname:"cpp")
}
}
// 创建完成后,编译器会自动提示需要在创建一个与之对应的 c++ 方法
external fun stringFromJni():String
创建native-lib.cpp标准文件,上述定义的本地方法,可以按照编辑器提示,自动创建实现方法
具体的实现方法遵循的方法名称是规定好的,可以参考以下例子
// src\main\cpp\native-lib.cpp
extern "C"
JNIEXPORT jstring JNICALL
Java_com_monk_cpp_ActMain_stringFromJni(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
来分析分析这个cpp文件
// src\main\cpp\native-lib.cpp
#inlcude <jni.h> // 定义了 jni 所支持的类型与接口
#inlcude <string>
extern "C"
JNIEXPORT jstring JNICALL// 这里的 jstring 类似于java中方法返回值,这里表示该c++方法返回的是字符串
Java_com_monk_cpp_ActMain_stringFromJni(JNIEnv *env, jobject thiz) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
其中JNIEXPORT jstring JNICALL语句,表示的c++函数的返回,其中jstring与java声明的native 方法是对应关系,这里也表面java中的对应函数返回的也是string
// src\main\java\com\monk\cpp\ActMain.kt
override fun initData(savedInstanceState: Bundle?) {
mBinding.test.text = "${stringFromJni()}\n" +
"${callJavaStaticMethod()}\n"
}
external fun callJavaStaticMethod(): String
companion object {
init {
System.loadLibrary("cpp")
}
@JvmStatic
fun staticMethod(cppStr: String): String {
Toast.makeText(App.INSTANCE, "i am java static method: $cppStr", Toast.LENGTH_LONG).show()
return "i am java static method: $cppStr"
}
}
对应的 c++ 函数
// src\main\cpp\nativ-lib.cpp
// 演示 c++ 调用 java 静态方法
extern "C"
JNIEXPORT jstring JNICALL
Java_com_monk_cpp_ActMain_callJavaStaticMethod(JNIEnv *env, jobject thiz) {
// 找到对应的类
jclass cls_main = env->FindClass("com/monk/cpp/ActMain");
// 获取methodId,kotlin需要@JvmStatic才能识别,最后一个参数是字节码中的显示,例如String的字节码为:(Ljava/lang/string;)V
jmethodID method_static_id = env->GetStaticMethodID(cls_main, "staticMethod", "(Ljava/lang/String;)Ljava/lang/String;");
// 构建String 变量
jstring str = env->NewStringUTF("来自c++的字符串");
// 调用java的static方法
env->CallStaticObjectMethod(cls_main, method_static_id, str);
//释放
env->DeleteLocalRef(cls_main);
// env->DeleteLocalRef(str);
return str;
}
c++如何调用 java 静态方法,使用GetStaticMethodID函数获取java的静态方法,使用CallStaticObjectMethod调用java静态方法

// src\main\java\com\monk\cpp\ActMain.kt
override fun initData(savedInstanceState: Bundle?) {
mBinding.test.text = "${stringFromJni()}\n" +
"${callJavaStaticMethod()}\n"+
"${callJavaMethod("monk", 24)}\n"
}
external fun callJavaMethod(name: String, age: Int): Any
// src\main\java\com\monk\cpp\JavaBean.kt
data class JavaBean(val name: String, val age: Int){
override fun toString(): String {
return "JavaBean(name='$name', age=$age)"
}
}
对应的 c++ 代码
// src\main\cpp\native-lib.cpp
extern "C"
JNIEXPORT jobject JNICALL
Java_com_monk_cpp_ActMain_callJavaMethod(JNIEnv *env, jobject thiz,jstring name, jint age) {
jclass java_bean = env->FindClass("com/monk/cpp/JavaBean");
// 获取构造方法
jmethodID init = env->GetMethodID(java_bean, "" , "(Ljava/lang/String;I)V");
// 调用构造方法
jobject bean = env->NewObject(java_bean, init, name, age);
//获取 toString 方法
jmethodID to_string = env->GetMethodID(java_bean, "toString", "()Ljava/lang/String;");
// 调用toString 方法
jobject result = env->CallObjectMethod(bean, to_string);
// 回收
env->DeleteLocalRef(java_bean);
// env->DeleteLocalRef(to_string);
return result;
}

引用上面例子中进行解释
// src\main\cpp\native-lib.cpp
extern "C"
JNIEXPORT jobject JNICALL
Java_com_monk_cpp_ActMain_callJavaMethod(JNIEnv *env, jobject thiz,jstring name, jint age) {
...
// 调用toString 方法,这里使用一个 jobjcet 创建一个局部引用
jobject result = env->CallObjectMethod(bean, to_string);
return result;
}
当然也可以将局部引用转成全局引用,在成员中声明即可
CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。
以前做 NDK 开发都是基于 Android.mk、Application.mk 来构建项目的,但从 AS 2.2 之后便开始采用CMake 的这种方式来构建,采用 CMake 相比与之前的 Android.mk、Application.mk 方便简单了许多
具体语法不过多涉及