使用JNI调用或使用Openfeint更改活动会导致应用程序崩溃 当我想用我的C++代码中的JNI调用来改变我的Android应用程序的活动时,我遇到了一个很大的问题。该应用程序使用cocos2d-x进行渲染。 具体情况是,我想使用以下非常小的函数在Java中打开OpenFeint仪表板: void launchOpenFeintDashboard() { Dashboard.open(); } 这个函数从C++调用,用简单的JNI调用: void OFWrapper::launchDashboard() { // init openfeint CCLog("CPP Init OpenFeint Dashboard"); CCDirector::sharedDirector()->pause(); jmethodID javamethod = JNIManager::env()->GetMethodID(JNIManager::mainActivity(), "launchOpenFeintDashboard", "()V"); if (javamethod == 0) return; JNIManager::env()->CallVoidMethod( JNIManager::mainActivityObj(), javamethod ); CCLog("CPP Init OpenFeint Dashboard done"); }

使用JNI调用或使用Openfeint更改活动会导致应用程序崩溃 当我想用我的C++代码中的JNI调用来改变我的Android应用程序的活动时,我遇到了一个很大的问题。该应用程序使用cocos2d-x进行渲染。 具体情况是,我想使用以下非常小的函数在Java中打开OpenFeint仪表板: void launchOpenFeintDashboard() { Dashboard.open(); } 这个函数从C++调用,用简单的JNI调用: void OFWrapper::launchDashboard() { // init openfeint CCLog("CPP Init OpenFeint Dashboard"); CCDirector::sharedDirector()->pause(); jmethodID javamethod = JNIManager::env()->GetMethodID(JNIManager::mainActivity(), "launchOpenFeintDashboard", "()V"); if (javamethod == 0) return; JNIManager::env()->CallVoidMethod( JNIManager::mainActivityObj(), javamethod ); CCLog("CPP Init OpenFeint Dashboard done"); },android,c++,android-ndk,java-native-interface,cocos2d-x,Android,C++,Android Ndk,Java Native Interface,Cocos2d X,JNIManager类实现也非常简单和基本: #include "JNIManager.h" #include <cstdlib> static JNIEnv* sJavaEnvironment = NULL; static jobject sMainActivityObject = NULL; static jclass sMainActivity = NULL; extern "C" { JNIEXPORT void JNICALL Java_net_plazz_

JNIManager类实现也非常简单和基本:

#include "JNIManager.h"
#include <cstdlib>

static JNIEnv* sJavaEnvironment = NULL;
static jobject sMainActivityObject = NULL;
static jclass  sMainActivity = NULL;


extern "C" {
    JNIEXPORT void JNICALL Java_net_plazz_mainzelapp_mainzelapp_sendJavaEnvironment(JNIEnv* env, jobject obj);
};

// this function is called from JAVA at startup to get the env
JNIEXPORT void JNICALL Java_net_plazz_mainzelapp_mainzelapp_sendJavaEnvironment(JNIEnv* env, jobject obj)
{
sJavaEnvironment = env;
sMainActivityObject = obj;
sMainActivity = JNIManager::env()->GetObjectClass(obj);
}



JNIEnv* 
JNIManager::env() 
{
return sJavaEnvironment;
}

jobject 
JNIManager::mainActivityObj() 
{
return sMainActivityObject;
}

jclass 
JNIManager::mainActivity() 
{
return sMainActivity;
}
这就引出了我要说的一点,即Android或Cocos2d-x在异步执行某些操作(更新成就)或结合使用NDK(我使用NDKr7,但在NDKr5上相同)更改活动时存在一些问题

您还应该知道,我已经在Java中定义了一些其他函数,这些函数通过JNI调用调用,并且可以正常工作

也许我做错了什么, 有人能给我一些建议,或者一个如何改变活动的工作示例代码吗。可能是Cocos2d-x的问题


谢谢。

我已经找到了我的问题的答案。它很容易修复,但很难找到。当应用程序更改为新活动时,将调用cocos2d-x MessageJNI中的nativeOnPause方法。这个方法应该调用CCApplication::sharedApplication(),但我的一个类以前调用过CCApplication析构函数,它将共享单例清除为null


编辑:这是对原始帖子的完整编辑,因此评论不再有意义。

我不知道Dalvik的实现,但@Goz是正确的:JNIEnv指针的作用域仅适用于您调用的JNI函数的持续时间。如果您想从本机代码回调到Java,您不能只保存上一次调用中的JNIEnv,因为该调用可能不再有效(因此AppCrash)。如果每件事都只有一个线程,那么它就可以工作了

您需要做的(如果您有多个线程)是在每次回调时获取一个有效的JNIEnv指针。在初始化功能中,保存指向当前正在运行的虚拟机的指针:

JavaVM *jvm;
env->GetJavaVM(&jvm);
然后,您可以使用此对正在运行的虚拟机的引用,通过调用以下命令获取有效的JNIEnv指针:

JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
然后您就可以使用env了,当您完成后,不要忘了打电话

jvm->DetachCurrentThread();
attach/detach将导致Java向thread对象注册调用方(可以是本机线程),从而允许将其视为Java线程


免责声明:至少在桌面Java中是这样做的。不知道Dalvik的实现,但从外观上看,他们只是复制了该技术。

AFAIK JNI env仅在函数调用处于活动状态时有效。。。您也不能保证对象仍然有效。基本上,我们需要看看你如何在launchDashboard调用中结束。ie从最初的条目到来自java的本机…为什么您有多个
活动
?你看过这个教程了吗:@Goz可能吧,但是你提供的代码还是有缺陷的。您正在JNIManager类中保存一个JNIEnv指针,然后在launchDashboard()方法中使用该指针。如果该方法被另一个线程或甚至另一个JNI函数调用,那么您将得到appcrash。尽管问题似乎与cocos或OpenFeint有关,但(我猜)您需要使用一些JNI结构(可能是JNIEnv指针)初始化这些库。如果你传递了一个无效的指针,那么应用程序将在库中崩溃。问题不是我。我的代码实现是不同的。那么也许你可以发布你实际有问题的代码。我正在创建一个新的问题,这将是最好的。我很确定你说的对android也是正确的。。。有没有一种方法可以保存一个对象(即阻止它也从另一个线程获取GC'd?)
jvm->DetachCurrentThread();