Android JNI之动态注册(android studio)

故事得从这里开始-静态注册

没有对比,就没有伤害。静态注册自出生以来,就非议颇多。繁琐的过程(javah生成头文件),每增加一个接口,渣握h一下。长长长的函数名称,一个详细的身份证明。运行效率较低,第一次进行交流的时候,根据详细的身份去jni中查找对应身份的人,这个难度比较大,一个一个去对比,运气好,一次匹配就建立了关系,如果,假如说如果,等到最后,黄花菜都凉了。艰难的时候,到了要放弃的时候,man是时候登场了,动态注册,对,就是他。JNI中有一个函数映射表,注册给Jave虚拟机,一见钟情,交流就是这么顺畅。

动态注册,画了几个圈

规则,还是要有的。早早建立数据库,交流一触即发。

一。入口 JNI_OnLoad 方法

System.loadLibrary加载完 JNI 动态库之后,调用JNI_OnLoad函数,开始动态注册。这里就是报名注册的地方,地方不能找错,你报个警抓小偷,非要打个119,我也很悲伤,但无能为力。注意:一个.so只能存在一个onload方法。

二。重头戏 RegisterNatives 方法

在 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_OnUnload 方法

出来混的迟早都是要还的,庞大的 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对象的实例。动态注册,就是这么回事,是时候展示实力的时候了。

动态注册项目结构图

猜你喜欢

转载自blog.csdn.net/youyi300200/article/details/72841441