关于android:JNI静态注册与动态注册

5次阅读

共计 2414 个字符,预计需要花费 7 分钟才能阅读完成。

导读

咱们晓得通过 Java 通过 JNI 能够调用 C /C++ 代码,C/C++ 也能够通过 JNI 调用 java 代码,那么 JNI 是怎么将 Java 办法与 Native 的办法对应起来的呢?

JVM 查找 Native 办法有两种形式:
1、依照 JNI 标准的命名规定进行查找,这种形式叫动态注册。
2、调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中,这种形式叫动静注册。

动态注册

所谓动态注册就是依照 JNI 标准书写函数名:

java_类门路_办法名(门路用下划线分隔)

当咱们应用 Android Studio 新建一个 Native 工程时默认生成的 JNI 函数就是动态注册的,例如上面就是一个动态注册的简略例子:

extern "C" JNIEXPORT jstring JNICALL
Java_com_fly_jnitest_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject obj) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

动态注册的形式是零碎的默认形式,应用简略,然而灵活性比拟差,如果批改了 Java 中的 Native 函数所在类的包名或类名,则须要同时批改 C /C++ 函数名称(头文件、源文件等)。

属性描述符与函数描述符

在理解动静注册之前,咱们先来理解 JNI 中的属性描述符和函数签名的概念。

JNI 属性描述符也就是变量类型在 JNI 中的示意形式,它是由属性的申明类型决定的。例如应用 ”I” 示意 int 属性,应用 ”F” 示意 float 属性,应用 ”D” 示意 double 属性,应用 ”Z” 示意 boolean 属性等。

对于援用各类型属性的描述符,比方 java.lang.String,须要以字母 ”L” 结尾,解析来是 JNI 类描述符并应用一个分号完结,Java 中残缺类名中的 ”.” 被 ”/” 替换掉了。因而,对于 java.lang.String 类型须要应用以下模式的属性描述符:
Ljava/lang/String;

数组类型的描述符由 ”[“ 以及数组元素类型的描述符组成,例如,[I示意整型数组的属性描述符,以此类推。

咱们能够应用 javap 工具从 class 文件生成属性描述符。

与属性描述符相似,函数也有函数描述符,通常咱们又称为函数签名,一个函数描述符由他的参数类型和返回值类型组成,参数类型在前,且应用一对括号括起来,参数类型是以他们在函数申明中的程序列举的,多个参数类型之间是没有分隔符,如果一个办法没有参数,应用一对空的括号示意即可。函数的返回值类型紧跟在包裹参数类型的右括号后边。

例如 (I)V 代指接管一个整型参数且返回值为空的函数。()D代指的是没有输出参数,返回值是一个 double 类型的函数。

留神:不要被 C 函数中像 ”int f(void)” 这样的函数原型误导,误认为 ”(V)I” 是它的办法描述符,其实 ”()I” 才函数 f 的函数描述符。

动静注册

在库加载时会主动调用 JNI_OnLoad() 函数,开发者常常会 JNI_OnLoad() 函数做一些初始化操作,动静注册就是在这里进行的。调用 API 是env->RegisterNatives(clazz, gMethods, numMethods)

env->RegisterNatives(clazz, gMethods, numMethods)是一个承受三个参数的函数,第一个参数是 Java 对应的类,第二个参数是 JNINativeMethod 数组,第三个参数是 JNINativeMethod 数组的长度,也就是须要注册的办法的个数。
其中 JNINativeMethod 示意的是办法办法的映射关系,它包含 Java 中的办法名,对应的办法签名和 Native 映射的函数办法三个信息。

相比动态注册,动静注册的灵活性更高,如果批改了 java native 函数所在类的包名或类名,仅调整 Java native 函数的签名信息即可。

以下展现了一个动静注册的例子:

// 前两个参数还是固定的
jstring stringFromJNI(JNIEnv *jniEnv,jobject jobj){return jniEnv->NewStringUTF("hello from C++ string");
}

static const JNINativeMethod nativeMethod[] = {
        // Java 中的函数名
        {"stringFromJNI",
         // 函数签名信息
         "()Ljava/lang/String;",
         // native 的函数指针
         (void *) (stringFromJNI)},
};

// 类库加载时主动调用
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reversed)
{
    JNIEnv *env = NULL;
    // 初始化 JNIEnv
    if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){return JNI_FALSE;}
    // 找到须要动静动静注册的 Jni 类
    jclass jniClass = env->FindClass("com/fly/jnitest/MainActivity");
    if(nullptr == jniClass){return JNI_FALSE;}
    // 动静注册
    env->RegisterNatives(jniClass,nativeMethod,sizeof(nativeMethod)/sizeof(JNINativeMethod));
    // 返回 JNI 应用的版本
    return JNI_VERSION_1_6;
}

相比于动态注册,动静注册不用在每次运行调用 Native 办法都去进行办法查找,所以相对来说动静注册的性能更高一些。

举荐浏览

JNI 根底简介
JNI 之数组与字符串的应用

关注我,一起提高,人生不止 coding!!!

正文完
 0