动态注册的原理
JNI 允许我们提供一个函数映射表,注册给 JVM,这样 JVM 就可以用函数映射表来调用相应的函数,而不必通过函数名来查找相关函数
1 | // java 代码中的函数是 dynamicRegFromJni,调用的 native 方法是 nativeDynamicRegFromJni,该方法没有参数, 返回值是一个字符串。 |
函数映射表
JNINativeMethod
这是一个结构体,在 jni.h 头文件中定义:
1 | typedef struct { |
Java 与 jni 通过该结构体建立联系,其中有三个变量:
- name:Java 中函数的名字。
- signature:签名符号,描述了函数的参数和返回值
- fnPtr:函数指针,指向一个被调用的函数
example
例如
1 | JNINativeMethod nativeMethod[] = {{"dynamicRegFromJni", "()Ljava/lang/String;", (void*)nativeDynamicRegFromJni}}; |
可以看出,里面有一个成员,该成员第一个参数 “dynamicRegFromJni”,java 函数名;第二个参数“()Ljava/lang/String:”,是签名符号,意思是该函数没有参数,返回一个字符串 ;第三个参数就是要调用的 native 方法。
其他例子:
1 | /* |
JNI_OnLoad()函数
当 java 通过 System.loadLibrary 加载完 JNI 动态库后,紧接着会调用 JNI_OnLoad 的函数。
RegisterNatives
动态注册的工作就是在这里完成的。
RegisterNatives在 jni.h 中是这么定义的:
c++
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
该函数有三个参数:
clazz: java 类名,通过 FindClass 得到
methods: JNINativeMethod 结构体指针
nMethods: 方法个数
调用:
1 | (*env) -> RegisterNatives(env, clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0])); |
c
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
该函数有四个参数:JNIEnv*
: env
clazz: java 类名,通过 FindClass 得到
methods: JNINativeMethod 结构体指针
nMethods: 方法个数
调用:
1 | env -> RegisterNatives(clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0])); |
c和c++
1 |
|
这是因为在jni.h中,c++的JNIEnv是_JNIEnv,而c的是JNINativeInterface*
1 | struct _JNIEnv { |
在c++中,实际上调用的是functions->RegisterNatives(this, clazz, methods, nMethods)
而functions是JNINativeInterface*
类型的指针。
1 | struct JNINativeInterface { |
c中,JNIEnv *env
,实际上是JNINativeInterface **
,这样的NINativeInterface的二级指针。
所以(*env) -> RegisterNatives(env, clz, nativeMethod, sizeof(nativeMethod) / sizeof(nativeMethod[0]))
就是先通过解引用得到JNINativeInterface *
结构体指针,再通过它调用jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,jint);
这个函数指针来使用函数。
tip:之所以是函数指针,是因为c中结构体成员中不能有函数