一、jni原理
主要就是通过数据类型签名和反射来实现java与c/c++方法进行交互的
数据类型签名对应表
java c/c++ boolean Z byte B char C short S int I long L float F double D void V object L开头,然后以/分割包的完整类型,后面再加; 比如String的签名就是Ljava/long/String Array 以[开头,在加上数组元素类型的签名, 比如int[],签名是[I,int[][]的签名是[[I, object[]的签名是[Ljava/lang/Object
数据类型对应表
java c/c++ boolean jboolean byte jbyte char jchar short jshort int jint long jlong float jfloat double jdouble void void
引用类型对应表
java c/c++ string jstring object jobject class jclass byte[] jbyteArray
二、实现jni
增加这三处
ndk{// 设置支持的架构abiFilters 'armeabi-v7a', 'arm64-v8a'}externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')version '3.18.1'}}
CMakLists.txt
# 声明并命名项目。
cmake_minimum_required(VERSION 3.18.1)# 声明并命名项目。
project("myc")# 创建并命名一个库,设置其类型为静态或共享,并提供其源代码的相对路径。
# 您可以定义多个库,CMake会为您构建它们。
# Gradle会自动将共享库与您的APK一起打包。
add_library( # 设置库的名称。myc# 设置库为共享库。SHARED# 提供源文件的相对路径。native-lib.cpp )# 在NDK中查找指定预构建的库,并将路径存储在变量中。
# 因为CMake默认将系统库添加到搜索路径中,所以您只需要指定公共NDK库的名称。
# CMake验证库是否存在,并在构建完成后完成其构建。
find_library( # 设置路径变量的名称。log-lib# 指定要CMake定位的NDK库名称。log )# 指定目标库应链接的库。您可以链接多个库,例如在此构建脚本中定义的库、预构建的第三方库或系统库。
target_link_libraries( # 指定目标库。myc# 将目标库与log库(在NDK中)链接。\${log-lib} )
native-lib.cpp里面就可以写C/C++代码了
#include <jni.h>
#include <string>/*** jni静态注册native方法*/
//extern "C" {
// JNIEXPORT jstring JNICALL Java_com_example_myc_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
// std::string hello = "Hello from C++";
// return env->NewStringUTF(hello.c_str());
// }
//
// JNIEXPORT jint JNICALL Java_com_example_myc_MainActivity_intFromJNI(JNIEnv *env, jobject /* this */, jint a, jint b) {
// return a + b;
// }
//}
/*** jni动态注册native方法*/
extern "C" {JNIEXPORT jstring JNICALL cpp_stringFromJNI(JNIEnv *env, jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());}JNIEXPORT jint JNICALL cpp_intFromJNI(JNIEnv *env, jobject obj/* this */, jint a, jint b) {/*** c++ 调用java方法*/jobject m_object = env->NewGlobalRef(obj);//创建对象的本地变量jclass myClass = env->FindClass("com/example/myc/MainActivity");//找到类文件jmethodID myMethod = env->GetMethodID(myClass, "myMethod", "(Ljava/lang/String;)V");//在类文件下找到方法,第三个参数是方法的签名也就是方法的参数类型和返回类型env->CallVoidMethod(m_object, myMethod, env->NewStringUTF("c数据"));return a + b;}
}//包名+类名字符串定义:
const char *class_name = "com/example/myc/MainActivity";
// 重点:函数表,相当于绑定,如果有多个方法要动态注册,在数组里面定义即可,第一个参数是java定义的函数名,第二个是函数签名,第三个是函数指针(也就是在c++自定义的函数名)
static const JNINativeMethod methods[] = {{"stringFromJNI", "()Ljava/lang/String;", (void *) cpp_stringFromJNI},{"intFromJNI", "(II)I", (void *) cpp_intFromJNI},
};
// 定义注册方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
// LOGD("动态注册");JNIEnv *env;if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
// LOGD("动态注册GetEnv fail");return JNI_ERR;}// 获取类引用jclass clazz = env->FindClass(class_name);// 注册native方法,sizeof(methods) 表示 methods 数组占用的总字节数,sizeof(methods[0]) 表示 methods[0] 类型占用的字节数。将这两个值相除,得到 methods 数组中元素的数量。jint register_result = env->RegisterNatives(clazz, methods,sizeof(methods) / sizeof(methods[0]));if (register_result) { // 非零true 进if
// LOGD("动态注册 fail regist_result = %d", regist_result);} else {
// LOGI("动态注册 success result = %d", regist_result);}return JNI_VERSION_1_6;
}void myMethod(JNIEnv *env, jobject obj) {jclass stringClass = env->FindClass("java/lang/String");jmethodID constructMethod = env->GetMethodID(stringClass, "<init>", "()V");jobject stringObj = env->NewObject(stringClass, constructMethod);std::string argStr = std::to_string(5);jstring result = (jstring) env->NewStringUTF(argStr.c_str());jobjectArray args = env->NewObjectArray(2, stringClass, stringObj);env->SetObjectArrayElement(args, 0, result);env->SetObjectArrayElement(args, 1, stringObj);
}
activity中
class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate var b = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)// binding.sampleText.text = stringFromJNI()// java调用c++方法binding.sampleText.text = intFromJNI(1, b).toString()findViewById<Button>(R.id.btn_jia).setOnClickListener {binding.sampleText.text = intFromJNI(1, ++b).toString()}}/*** c++调用java方法*/fun myMethod(str:String){Toast.makeText(this,"来自c端数据=$str",Toast.LENGTH_LONG).show()}/*** java调用c++方法*/external fun stringFromJNI(): Stringexternal fun intFromJNI(a: Int, b: Int): Intcompanion object {// 导入c++库与CMakeLists中的库的命名一致init {System.loadLibrary("myc")}}
}