没有对比,就没有伤害。静态注册自出生以来,就非议颇多。繁琐的过程(javah生成头文件),每增加一个接口,渣握h一下。长长长的函数名称,一个详细的身份证明。运行效率较低,第一次进行交流的时候,根据详细的身份去jni中查找对应身份的人,这个难度比较大,一个一个去对比,运气好,一次匹配就建立了关系,如果,假如说如果,等到最后,黄花菜都凉了。艰难的时候,到了要放弃的时候,man是时候登场了,动态注册,对,就是他。JNI中有一个函数映射表,注册给Jave虚拟机,一见钟情,交流就是这么顺畅。
规则,还是要有的。早早建立数据库,交流一触即发。
System.loadLibrary加载完 JNI 动态库之后,调用JNI_OnLoad函数,开始动态注册。这里就是报名注册的地方,地方不能找错,你报个警抓小偷,非要打个119,我也很悲伤,但无能为力。注意:一个.so只能存在一个onload方法。
在 JNI 帝国中,不得不介绍下 JNIEnv*,JVM代言人,掌握java生杀大权,java环境变量指针,是一个包含了JVM接口的结构,它包含了与JVM进行交互以及与Java对象协同工作所必需的函数。而 RegisterNatives 只是其中的一颗棋子,登记员。但只有通过他,你才能进入函数映射表。函数映射表?不急,我们慢慢来,一步一步剥离他的心。在 jni.h 帝国中,描述有这么一段:
typedef struct {
const char* name; //Java中函数的名字
const char* signature; //描述了函数的参数和返回值
void* fnPtr; //函数指针,指向我们调用别人家c++的封装 JNI 函数方法
} JNINativeMethod;
这就是函数映射表的数据结构,注释加上,似乎也没什么需要解释的了,至于繁琐的源代码解析,这个结构又是怎么被使用,脑补,脑补。咳咳咳,回归正题,重头戏 RegisterNatives,这是一部宫廷史诗剧,精心的布局,宏大的场面。这么说吧,演员甲乙丙,就三个,共同出演一部不是你死就是我死的故事结局。剧本中记载了这么一段:
/**
* 向JNI环境注册一个本地方法
* @param clazz 包含本地方法的Java类
* @param methods 本地方法描述数组
* @param nMethods 本地方法个数
* @return 成功返回0,否则注册失败
*/
jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods);
不知不觉,后知后觉,一个注释完成了解析。简单点,说话的方式简单点。该配合你演出的我演视而不见。这个登记员的描述,大体如此了,如有更多需求,请自行研究。
出来混的迟早都是要还的,庞大的 JNI 帝国也有结束的那一天。当VM释放该组件时会调用 JNI_OnUnload 方法,曾经拥有的,比如说对象啊,通通在这里回归大地,化作春泥更护花。 当然也可以选择放弃,这个方法甩在一边,那么毒蘑菇总有一天会茁壮成长的,慢慢慢慢,然后boom。优化,是你我的一个可选项。
不得不说,这个时刻,我特别激动。花儿,为什么这么红?略微贴下代码,稍显细节:
static const char *jniClassName = "net/mapout/jni/JNILoader";
static JNINativeMethod methods[] = {
{"sayHello", "()Ljava/lang/String;", (void*)jniSayHello},
};
static int registerNatives(JNIEnv* env) {
jclass clazz = env->FindClass(jniClassName);
if (clazz == NULL)
return JNI_FALSE;
jint methodSize = sizeof(methods) / sizeof(methods[0]);
if ( env->RegisterNatives(clazz, methods, methodSize) < 0 )
return JNI_FALSE;
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
//注册方法
if (!registerNatives(env))
return JNI_ERR;
result = JNI_VERSION_1_6;
return result;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
jint ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
if (ret != JNI_OK) {
return ;
}
//回收二手女朋友,回收... 嘿嘿嘿
}
一目了然,我们所需要维护的就是methods这个数组了,剩下的都是复制粘贴。稍微来个对比:
/**
* 动态注册。命名简洁,清晰明了
*/
jstring jniSayHello(JNIEnv *env, jobject obj){
return str2jstring( env, sayHello() );
}
/**
* 静态注册。噗噗噗,一口老血喷涌而出
*/
JNIEXPORT jstring JNICALL Java_net_mapout_jni_JNILoader_sayHello
(JNIEnv *env, jclass jclass){
return str2jstring( env, sayHello() );
}
又是一个注释。代码,没有一个注释不能说清楚的,如果有,那就请用两个注释。说个细节,这里函数都有2个参数,一个 JNIEnv * 已经解释过了,还一个jobject。稍微讲一下,native方法如果没有static,那么就是类的实例,如果武装了static,进化加强,变为了类的class对象的实例。动态注册,就是这么回事,是时候展示实力的时候了。