接上一篇博客:在Android平台初学JNI踩过的几个小坑
上一篇记录了初学JNI时踩得坑,终于是在JNI层成功调用了Android 的Java API 弹出了Toast, JNI的编写格式使用了传统的方式,这一篇记录一下动态注册JNI函数的过程。
直接在代码上注释含义。
只改动了native-lib.c的代码,其它代码没有改动:
#include <jni.h> #include "MLog.h" #define TAG "NativeActivity" // 这个是实际执行业务逻辑的函数,业务逻辑和上一篇提到的没有差别,只是函数名称可以随意写 JNIEXPORT void JNICALL native_showToast(JNIEnv* env, jobject instance,jstring str) { jclass Toast = (*env)->FindClass(env,"android/widget/Toast"); jmethodID makeText = (*env)->GetStaticMethodID(env,Toast, "makeText", "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;"); jstring msg = (*env)->NewStringUTF(env, "太神奇了哈哈"); jobject Toast2 = (*env)->CallStaticObjectMethod(env,Toast, makeText, instance, msg); jmethodID show = (*env)->GetMethodID(env,Toast, "show", "()V"); (*env)->CallVoidMethod(env,Toast2, show); Android_LOG_I(TAG, "showToast:"); } // 这个数组里面定义了Java中声明的native方法和JNI层方法的映射,可以声明多个函数映射,这里只写了一个 static const JNINativeMethod methodTable[] = {
// 大括号{}里面是JNINativeMethod结构体类型的数据,依次是{"Java native方法名", "Java native方法签名", "JNI对应的函数的函数指针"} {"showToast", "(Ljava/lang/String;)V", (void*)native_showToast} }; // 关联Java层和JNI层方法的逻辑,主要由jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)函数实现 jint register_com_yolo_jnidemo_MainActivity(JNIEnv *env) { jclass MainActivity = (*env)->FindClass(env,"com/yolo/jnidemo/MainActivity");
// 注册函数的参数(JNIEnv类型指针,Java层方法所在类,函数映射列表,函数个数) return (*env)->RegisterNatives(env, MainActivity, methodTable, 1); }
// 该函数在System.loadLibrary()方法加载so库后回调 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv *env = NULL; // 通过JavaVM来获取JNIEnv实例,将获取结果传给env变量 if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) { Android_LOG_E(TAG, "get JNIEnv failed."); return -1; } // 获取JNIEnv成功后就可以调用注册函数进行Java层和JNI层关联,关联成功后我们在Java层调用native方法就会执行JNI层对应函数的逻辑 register_com_yolo_jnidemo_MainActivity(env); Android_LOG_D(TAG, "jni onload."); return JNI_VERSION_1_4; }
// 该函数在JNI库被卸载时回调,可以执行一些清理逻辑 JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { Android_LOG_D(TAG, "jni onunload."); }
以上函数的书写顺序是由于C代码需要先声明后使用,因此一些后执行的逻辑写在了前面,实际的执行顺序是:
JNI_OnLoad -> register_com_yolo_jnidemo_MainActivity -> methodTable -> native_showToast
在这里很希望一些同学发现问题后能指出来,将不正确的知识点完善起来,共同进步。