JNI内部类方法中接收外部类this而非内部类this的问题
JNI中内部类调用外部类native方法时
this参数为外部类实例的原因及解决方案 原因分析:这是符合规范的有意设计
你遇到的现象完全符合JNI和Java语言规范,核心原因如下:
jni_print是外部类OuterClass的成员native方法,JNI中实例方法的第一个jobject this参数,始终指向该方法所属类的实例,而非调用该方法的对象实例。- Java非静态内部类默认持有外部类的引用,当内部类调用外部类的成员方法时,本质是通过持有的外部类实例来执行该方法。这和普通Java方法的行为完全一致:比如在
InnerClass里调用OuterClass的普通成员方法,方法内的this同样是外部类实例,而非内部类实例。
关于文档说明
JNI规范明确规定:实例native方法的第一个参数是方法所属类的对象引用(对应Java方法中的this);Java语言规范也定义了内部类访问外部类成员的机制。只需将两者结合理解,就能解释你遇到的现象。
解决方案
如果需要在JNI中获取内部类的实例,有两种可行方式:
- 手动传递内部类
this作为参数(即你当前代码的做法)
你已经在InnerClass.printThis()中把内部类的this传给jni_print,JNI里通过第二个参数obj就能拿到内部类实例,这是完全合理的实现方式。 - 将native方法定义在内部类中
修改代码把jni_print定义为InnerClass的native方法,此时JNI的this参数就会是内部类的实例:
对应的JNI注册代码需要调整为注册到public final class OuterClass { static { System.loadLibrary("jtest"); } static private void print(Object o1, Object o2) { System.out.println("this = " + o1 + ", arg1 = " + o2); } public OuterClass() { } public void printThis() { jni_print(this); } private native void jni_print(Object obj); public final class InnerClass { private native void jni_print_inner(Object obj); public InnerClass() { } public void printThis() { jni_print_inner(this); } } }OuterClass$InnerClass类:#include <stdio.h> #include <stdlib.h> #include <jni.h> #define OUTER_CLASS "OuterClass" #define INNER_CLASS "OuterClass$InnerClass" static void jni_print_outer(JNIEnv *env, jobject this, jobject obj) { jclass cls = (*env)->FindClass(env, OUTER_CLASS); jmethodID printID = (*env)->GetStaticMethodID(env, cls, "print", "(Ljava/lang/Object;Ljava/lang/Object;)V"); (*env)->CallStaticVoidMethod(env, cls, printID, this, obj); } static void jni_print_inner(JNIEnv *env, jobject this, jobject obj) { jclass cls = (*env)->FindClass(env, OUTER_CLASS); jmethodID printID = (*env)->GetStaticMethodID(env, cls, "print", "(Ljava/lang/Object;Ljava/lang/Object;)V"); (*env)->CallStaticVoidMethod(env, cls, printID, this, obj); } static JNINativeMethod outer_funcs[] = { { "jni_print", "(Ljava/lang/Object;)V", (void *)&jni_print_outer }, }; static JNINativeMethod inner_funcs[] = { { "jni_print_inner", "(Ljava/lang/Object;)V", (void *)&jni_print_inner }, }; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv *env; jclass outer_cls; jclass inner_cls; jint reg_res; (void)reserved; if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK) return -1; outer_cls = (*env)->FindClass(env, OUTER_CLASS); if (outer_cls == NULL) return -1; reg_res = (*env)->RegisterNatives(env, outer_cls, outer_funcs, sizeof(outer_funcs)/sizeof(outer_funcs[0])); if (reg_res != 0) return -1; inner_cls = (*env)->FindClass(env, INNER_CLASS); if (inner_cls == NULL) return -1; reg_res = (*env)->RegisterNatives(env, inner_cls, inner_funcs, sizeof(inner_funcs)/sizeof(inner_funcs[0])); if (reg_res != 0) return -1; return JNI_VERSION_1_8; } // JNI_OnUnload 代码略
内容的提问来源于stack exchange,提问作者PoltoS
相关产品推荐
相关产品推荐

