导读

咱们晓得通过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 JNICALLJava_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!!!