Android NDK学习(五):Java调用Native代码流程总结
一、概述
当 Java 调用 native 方法时,虚拟机是怎么知道该调用 so 中的哪个方法呢?这就需要用到注册了,通过注册,将指定的 native 方法和 so 中对应的方法绑定起来(函数映射表),这样就能够找到相应的方法了。
native 方法的注册方式分为静态注册 和 动态注册 两种。默认的实现方式即静态注册。
二、静态注册
静态注册通过 JNIEXPORT 和 JNICALL 两个宏定义声明方法,命名规则为:Java + 包名 + 类名 + 方法名, 其中使用下划线将每部分隔开,包名也使用下划线隔开,如果名称中本来就包含下划线,将使用下划线加数字替换。在虚拟机加载 so 时发现上面两个宏定义的函数时就会链接到对应的 native 方法。
例如:
// Java native methodpublic native String stringFromJNI();
// JNI method
JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_MainActivity_stringFromJNI( JNIEnv *env, jobject instance);
优点:
实现简单,在Android Studio上,能通过快捷键自动生成native方法。
缺点:
命名必须遵循注册规则,且存在名字过长的缺点。
三、动态注册
通过 RegisterNatives 方法手动完成 native 方法和 so 中的方法的绑定,这样虚拟机就可以通过这个函数映射表直接找到相应的方法了。
这里不得不提到一个方法:
extern "C"JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
jint result = -1;
javaVm = vm; // 赋值全局jvm
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return result;
}
return JNI_VERSION_1_6;
}
Java调用System.loadLibrary()加载一个库的时候,会首先在库中搜索JNI_OnLoad()函数,如果该函数存在,则执行它;
JNI_OnLoad()的作用主要有几点:
- 告诉JVM,这个库需要要求使用的JNI版本是什么
- 执行初始化操作
- 将JavaVM参数保存为全局对象,方便以后在任何地方获取JNIEnv对象
注意: JNI_OnLoad方法在每一个库中只能存在一个。 如果使用动态注册的方式进行native方法的注册,可以在此方法中进行操作。
如果使用动态注册就不需要再使用 JNIEXPORT 和 JNICALL 两个宏定义声明指定的方法,也不用依照固定的命名规则命名方法,而是通过了一个 RegisterNatives 方法完成了动态注册。
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
方法参数:
- clazz:指定的类,即 native 方法所属的类
- methods:方法数组,这里需要了解一下 JNINativeMethod 结构体
- nMethods:方法数组的长度
成功则返回 JNI_OK (0),失败则返回一个负值。
这里在说明一下JNINativeMethod的结构体构成:
typedef struct {const char* name; // native 的方法名
const char* signature; // 方法签名,例如 ()Ljava/lang/String;
void* fnPtr; // 函数指针
} JNINativeMethod;
优点:
1. 方便绑定的切换,重构代码时更为简单
2. 调用时无需再进行查找,对Native方法的函数名无要求
缺点:
1. 会导致无法通过Java Native方法自动关联跳转,需要自行查找。
以上是 Android NDK学习(五):Java调用Native代码流程总结 的全部内容, 来源链接: utcz.com/z/390003.html