JNI 函数的动态注册

354 阅读2分钟

1: 在C++文件中声明Java/kotlin 中Native 类的路径,注册的时候,会用到

注意:将 . g改成 "/ "

const char *NATIVE_CLASS_PATH = "com/ya/jnidynamicregister/NativeMethod";  

kotlin 中封装native 函数的代码


class NativeMethod {
    external fun getString(nameList: Array<String>, value: Int): String
    external fun execJniCallJava()

}

2: C++ 中的 native 函数


jstring getString(JNIEnv *env, jobject thisObj) {
    return env->NewStringUTF(string(",,晚安啦,,").c_str());
}

具体如下:

  • 声明navite 函数对应数组,数组类型:JNINativeMethod

    • JNINativeMethod 的结构体
        typedef struct { 
           // 在kotlin class 中声明的native函数名 
           const char* name;
           // 函数的签名,用android studio的Kotlin Bytecode 就可以查看,直接copy即可, 免得写错
           const char* signature;
           // 对应的native函数名 
           void* fnPtr;
        }JNINativeMethod
    
    • JNINativeMethod 的结构体 * 声明nativeMethods 数组

      JNINativeMethod nativeMethods[] = {
              {"getString", "()V", (void *) getString},
      };
      
    • JNI_OnLoad 中注册 在nativeMethods 数组,具体如下:

      • step 1:JNI_OnLoad
     jint JNI_OnLoad(JavaVM *vm, void *reserved) {
         JNIEnv *env = nullptr;
         LOGD("JNI_OnLoad ");
    
         if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
             return -1;
         }
    
         if (env == nullptr) return -1;
         if (!registerNatives(env)) {
             return -1;
         }
         return JNI_VERSION_1_6;
     }
    
    • step 2:registerNatives()
        int registerNatives(JNIEnv *env) {
            if (!registerNativeMethods(env, NATIVE_CLASS_PATH, nativeMethods,
                                   sizeof(nativeMethods) / sizeof(nativeMethods[0]))) {
                return JNI_FALSE;
            }
            return JNI_TRUE;
        }
    
    • step 3:registerNativeMethods()
      • 主要就是 jni 中的RegisterNatives() 函数
     int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *gMethods,
                                     int numMethods) {
        jclass clazz  = env->FindClass(className);
        if (clazz == nullptr)
            return JNI_FALSE;
    
        if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
            LOGE("registerNativeMethods in  error");
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    

3: 在android的Activity中调用

// 全局声明
private lateinit var nativeMethod: NativeMethod
// 在onCrete中声明
nativeMethod = NativeMethod()
// 在点击事件中使用
 nativeMethod.execJniCallJava()

静态注册和静态注册的区别

  • 静态:运行的时候 去so寻找 native 函数

    • 个人感觉: 编写,追踪函数调用挺方便的,native 还不爆红,
  • 动态: 编译加载so的时候就把函数进行注册,之后的调用可以直接使用

    • 个人感觉: 有些面向对象的感觉,将native函数封装在结构体数组里,然后一股脑的交给JVM;
    • 代码爆红,有点膈应;

Screen Shot 2022-07-08 at 18.31.30.png

Demo源码

参考