Android 无法从本机代码调用 Java 方法 JNI



我有一个本机代码,我想使用 JNI 从中调用一些 Java 方法,我的主要问题是FindClassJVM env在我的类的情况下总是返回 null 的方法虽然我写得正确,但如果任何像 String 这样的 Java 类它返回一个有效的指针,我得到了一个加载ClassLoader的示例代码并让它加载我的类,但不幸的是我得到了 NULL。 这是代码:

unsigned char* pixels = new unsigned char[3 * width * height];
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
std::string imgURL(dataPath+"/Levels/screenshot.png");
stbi_write_png(imgURL.c_str(), width, height, 3, pixels, width * 3);
stbi_image_free(pixels);
JNIEnv *lJNIEnv;
manager.activity->vm->AttachCurrentThread(&lJNIEnv, NULL);
jobject lNativeActivity =  manager.activity->clazz;
jclass ClassNativeActivity = lJNIEnv->FindClass("android/app/NativeActivity");
jclass contextClass = lJNIEnv->FindClass("android/content/Context");
if(contextClass == 0)
return;
jmethodID startActivityMethodId = lJNIEnv->GetMethodID(contextClass, "startActivity", "(Landroid/content/Intent;)V");
if(startActivityMethodId == 0)
return;
jclass intentClass = lJNIEnv->FindClass("android/content/Intent");
if(intentClass == 0)
return;
jmethodID intentConstructorMethodId = lJNIEnv->GetMethodID(intentClass, "<init>", "()V");
if(intentConstructorMethodId == 0)
return;
jmethodID intentSetActionMethodId = lJNIEnv->GetMethodID(intentClass, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;");
if(intentSetActionMethodId == 0)
return;
jmethodID getClassLoader = lJNIEnv->GetMethodID(ClassNativeActivity,"getClassLoader", "()Ljava/lang/ClassLoader;");
if(getClassLoader == 0)
return;
jobject cls = lJNIEnv->CallObjectMethod(lNativeActivity, getClassLoader);
if(cls == 0)
return;
jclass classLoader = lJNIEnv->FindClass("java/lang/ClassLoader");
if(classLoader == 0)
return;
jmethodID findClass = lJNIEnv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
if(findClass == 0)
return;
jstring intentString = lJNIEnv->NewStringUTF("com/game/Share");
if(intentString == 0)
return;
jobject intentObject = lJNIEnv->NewObject(intentClass,intentConstructorMethodId);
if(intentObject == 0)
return;
//Here I got NULL although the package and class are correct
jclass marketActivityClass = (jclass)lJNIEnv->CallObjectMethod(cls, findClass, intentString);
if(marketActivityClass == 0)
return;
lJNIEnv->CallVoidMethod(intentObject, intentSetActionMethodId,intentString);
lJNIEnv->CallVoidMethod(lNativeActivity, startActivityMethodId, intentObject);
manager.activity->vm->DetachCurrentThread();

有什么帮助吗?

您已被已知的 Android JNI 限制所困扰:

如果您自己创建线程(也许通过调用pthread_create然后使用 AttachCurrentThread 附加它(,您可能会遇到麻烦。现在,您的应用程序中没有堆栈帧。如果从此线程调用 FindClass,JavaVM 将在"系统"类装入器中启动,而不是在与应用程序关联的类装入器中启动,因此尝试查找特定于应用程序的类将失败。

遵循文章中列出的建议应该可以让您摆脱困境:

  • 在JNI_OnLoad中执行一次 FindClass 查找,并缓存类引用以供以后使用。作为执行JNI_OnLoad的一部分进行的任何 FindClass 调用都将使用与调用 System.loadLibrary 的函数关联的类加载器(这是一个特殊规则,提供该规则是为了使库初始化更方便(。如果应用代码正在加载库,FindClass 将使用正确的类加载器。
  • 将类的实例传递到需要它的函数中,方法是声明本机方法以获取 Class 参数,然后将 Foo.class 传入。
  • 将 ClassLoader 对象的引用缓存在方便的位置,并直接发出 loadClass 调用。这需要一些努力。

最新更新