当执行一个 Java 的 native 方法时,虚拟机是怎么知道该调用 so 中的哪个方法的?
Jvm记载so库
- 通过System.loadLibrary按名加载so库
- 若是linux操作系统so库名都是libxxx.so格式
- System.loadLibrary调用ClassLoader去查找
- 最终调用系统的库函数dlopen、dlsym实现加载到内存
静态注册
虚拟机加载 so 时发现JNIEXPORT和JNICALL两个宏定义的函数时就会链接到对应的“同名” native 方法。
// Java native method
public native String stringFrom_JNI();
// JNI method Java + 包名 + 类名 + 方法名
JNIEXPORT jstring JNICALL
Java_com_afei_jnidemo_MainActivity_stringFrom_1JNI(JNIEnv *env, jobject instance);
动态注册
通过 RegisterNatives 方法手动完成 native 方法和 so 中的方法的绑定,这样虚拟机就可以通过这个函数映射表直接找到相应的方法了。
extern "C" {
jstring stringFromJNI(JNIEnv *env, jobject instance) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
jint add(JNIEnv *env, jclass clazz, jint a, jint b) {
return a + b;
}
jint RegisterNatives(JNIEnv *env) {
jclass clazz = env->FindClass("com/afei/jnidemo/MainActivity");
if (clazz == NULL) {
LOGE("con't find class: com/afei/jnidemo/MainActivity");
return JNI_ERR;
}
JNINativeMethod methods_MainActivity[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"add", "(II)I", (void *) add}
};
// int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);
return env->RegisterNatives(clazz, methods_MainActivity,
sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]));
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jint result = RegisterNatives(env);
LOGD("RegisterNatives result: %d", result);
return JNI_VERSION_1_6;
}
}