【面向面试学习】Java Native方法与Native函数是怎么绑定的?

1,359 阅读1分钟

当执行一个 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;
}
}

静态注册与动态注册的区别

参考

JNI 静态注册和动态注册

Java JNI实现原理初探