Android 安卓可以';t从本机代码JNI调用Java方法

Android 安卓可以';t从本机代码JNI调用Java方法,android,c++,java-native-interface,classloader,Android,C++,Java Native Interface,Classloader,我有一个本机代码,我想使用JNI从中调用一些Java方法,我的主要问题是FindClassmethod ofJVM env在我的类中总是返回null,尽管我写得正确,但如果任何Java类(如字符串)返回有效指针,我得到了一个用于加载类加载器的示例代码,并让它加载我的类,但不幸的是,我得到了NULL。 代码如下: unsigned char* pixels = new unsigned char[3 * width * height]; glReadPixels(0, 0, width, heig

我有一个本机代码,我想使用JNI从中调用一些Java方法,我的主要问题是
FindClass
method of
JVM env
在我的类中总是返回null,尽管我写得正确,但如果任何Java类(如字符串)返回有效指针,我得到了一个用于加载
类加载器的示例代码,并让它加载我的类,但不幸的是,我得到了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();
无符号字符*像素=新的无符号字符[3*宽度*高度];
glReadPixels(0,0,宽度,高度,GL_RGB,GL_无符号字节,像素);
std::string imgURL(dataPath+“/Levels/screenshot.png”);
stbi_write_png(imgURL.c_str(),宽度,高度,3,像素,宽度*3);
无stbi_图像(像素);
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)
返回;
jmethodID startActivityMethodId=lJNIEnv->GetMethodID(contextClass,“startActivity”,Landroid/content/Intent;)V);
如果(startActivityMethodId==0)
返回;
jclass intentClass=lJNIEnv->FindClass(“android/content/Intent”);
如果(intentClass==0)
返回;
jmethodide intentconstructormethodide=lJNIEnv->GetMethodID(intentClass,“,”()V”);
if(intentConstructorMethodId==0)
返回;
jmethodID intentSetActionMethodId=lJNIEnv->GetMethodID(intentClass,“setAction”,“(Ljava/lang/String;)Landroid/content/Intent;”;
if(intentSetActionMethodId==0)
返回;
jmethodID getClassLoader=lJNIEnv->GetMethodID(ClassNativeActivity,“getClassLoader”,“()Ljava/lang/ClassLoader;”;
if(getClassLoader==0)
返回;
jobject cls=lJNIEnv->CallObjectMethod(lNativeActivity,getClassLoader);
如果(cls==0)
返回;
jclass classLoader=lJNIEnv->FindClass(“java/lang/classLoader”);
if(classLoader==0)
返回;
jmethodID findClass=lJNIEnv->GetMethodID(类加载器,“loadClass”,“Ljava/lang/String;)Ljava/lang/Class;”;
如果(findClass==0)
返回;
jstring intentString=lJNIEnv->NewStringUTF(“com/game/Share”);
if(intentString==0)
返回;
jobject intentObject=lJNIEnv->NewObject(intentClass,intentConstructorMethodId);
if(intentObject==0)
返回;
//虽然包和类是正确的,但这里我得到了NULL
jclass marketActivityClass=(jclass)lJNIEnv->CallObjectMethod(cls、findClass、intentString);
如果(marketActivityClass==0)
返回;
lJNIEnv->CallVoidMethod(intentObject、intentSetActionMethodId、intentString);
LJNINEV->CallVoidMethod(主动性、主动性、主动性、意向性);
manager.activity->vm->DetachCurrentThread();

有什么帮助吗?

您被已知的:

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

以下是文章中列出的建议,这些建议会让你放松:

  • 在JNI_OnLoad中查找一次FindClass,并缓存类引用以供以后使用。作为执行JNI_OnLoad的一部分进行的任何FindClass调用都将使用与调用System.loadLibrary的函数关联的类加载器(这是一条特殊规则,用于使库初始化更加方便)。如果应用程序代码正在加载库,FindClass将使用正确的类加载器
  • 通过声明本机方法以获取类参数,然后传入Foo.class,将类的实例传递给需要它的函数
  • 将对ClassLoader对象的引用缓存在方便的地方,并直接发出loadClass调用。这需要一些努力
你是说在你的评论之前的
GetMethodID()
调用返回空值,还是评论之后的
NewStringUTF()
返回空值?@zenzelezz你是对的,我编辑了它,它是
CallObjectMethod
你怎么知道你得到了
NULL
?请注意,如果出现错误,您没有正确地分离当前线程。您如何获得
manager.activity->clazz?把它也贴出来,看。使用
NewStringUTF(“com.game.Share”)
。请注意,在分离线程之前必须释放所有本地引用。narive活动中的
void android\u main(struct android\u app*state)
是否被视为单独的线程?因为当我把代码放在上面的时候,我也会得到Null,它是。但是,您可以从
app->activity->clazz
提取工作类加载器并使用选项3。参见示例(将
activity\u class
替换为
app->activity->clazz
,或将
manager.activity->clazz
替换为您所拥有的内容)。