美文网首页
JNI中native方法的几种注册方式

JNI中native方法的几种注册方式

作者: JasonChen8888 | 来源:发表于2020-05-19 15:12 被阅读0次

    背景

    面试NDK开发的时候,经常碰到一个问题:如何在jni中注册native函数,有几种注册方式?
    答案:native方法的注册分为静态注册和动态注册

    静态注册

    • 静态注册的原理
      原理:根据函数名来建立 java 方法与 JNI 函数的一一对应关系
    • 实现流程
      1.编写带有native声明的方法的java类
      2.编译生成class文件
      3.利用javah生成(.h)的头文件 命令:javah 类名, 注:不需要class后缀
      4.将(.h)头文件复制到vs下,创建(.cpp)或者(.c)文件实现(.h)头文件声明的方法
      5.实现完成后,编译成dll库
      6.将dll复制到java项目的根目录,调用System.loadLibrary("dll库名"); //注:不要dll后缀
      7.在代码里面调用native方法,访问native(.cpp 或者 .c)的代码
    • 具体实现
      https://www.jianshu.com/p/3fdf924680af

    动态注册

    • 动态注册的原理
      原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系
    • 实现流程
    1. 利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系;
    2. 实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册;
    3. 调用 FindClass 方法,获取 java 对象;
    4. 调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册;
    • 具体实现
      java代码:native方法的定义
    public native static String getStringFromJni();
    

    C++的代码

    #include "stdafx.h"
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    #include "jni.h"
    #include <assert.h>
    
    //定义的对应java中的定义native方法
    JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz)
    {
        printf("hello in c native code./n");
        return env->NewStringUTF("hello world returned to java");
    }
    
    //需要动态注册的native方法所在的类
    #define JNIREG_CLAS_MAIN "com/jason/jni/JniMain"
    
    //创建JNINativeMethod的数组,用来存放,JNINativeMethod结构变量,JNINativeMethod结构存放:注册的native方法,对应的签名,C++/C的对应的JNI方法
    static JNINativeMethod gMethods[] = {
        {"getStringFromJni","()Ljava/lang/String;", native_hello }
    };
    
    static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods) {
        jclass clazz;
        clazz = env->FindClass(className);
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
            return JNI_FALSE;
        }
        return JNI_TRUE;
    }
    
    /***
    * 注册native方法
    */
    static int registerNatives(JNIEnv* env) {
        if (!registerNativeMethods(env, JNIREG_CLAS_MAIN, gMethods, sizeof(gMethods) / sizeof(gMethods[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_4) != JNI_OK) {
            return -1;
        }
        assert(env != NULL);
    
        if (!registerNatives(env)) { //注册
            return -1;
        }
        result = JNI_VERSION_1_4;
        return result;
    }
    
    • JNINativeMethod的结构体
    typedef struct {
        char *name;
        char *signature;
        void *fnPtr;
    } JNINativeMethod;
    

    介绍:

    1. name:是java中定义的native方法名
    2. signature:是用于描述方法的参数与返回值,方法的签名
    3. fnPtr 是函数指针,用来指向 jni 函数

    区别:

    • 静态注册
      优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
      缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高
    • 动态注册
      优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
      缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

    相关文章

      网友评论

          本文标题:JNI中native方法的几种注册方式

          本文链接:https://www.haomeiwen.com/subject/xhlbohtx.html