使用JNI调用或使用Openfeint更改活动会导致应用程序崩溃
Change Activity with JNI Call or using Openfeint causes App-Crash
当我想用C++代码中的JNI调用来更改Android应用程序的"活动"时,我遇到了一个巨大的问题。该应用程序使用cocos2d-x进行渲染。具体情况是,我想使用这个非常小的函数打开Java中的OpenFeint Dashboard:
void launchOpenFeintDashboard() {
Dashboard.open();
}
然后用一个简单的JNI调用从C++调用这个函数:
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");
}
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;
}
从我的角度来看,cocos2d-x在使用JNI调用更改活动时存在一些循环问题,因为在将活动更改为任何自己的活动时,我也会遇到应用程序崩溃。
但是,当我简单地使用OpenFeint通过JNI调用更新成就时,我也会遇到应用程序崩溃,类似于更改活动时:
void updateAchievementProgress( final String achievementIdStr, final String progressStr ) {
Log.v("CALLBACK", "updateAchievementProgress (tid:" + Thread.currentThread().getId() + ")");
float x = Float.valueOf(progressStr).floatValue();
final Achievement a = new Achievement(achievementIdStr);
a.updateProgression(x, new Achievement.UpdateProgressionCB() {
@Override
public void onSuccess(boolean b) {
Log.e("In Achievement", "UpdateProgression");
a.notifyAll();
}
@Override
public void onFailure(String exceptionMessage) {
Log.e("In Achievement", "Unlock failed");
a.notifyAll();
}
});
Log.v("CALLBACK", "updateAchievementProgress done (tid:" + Thread.currentThread().getId() + ")");
}
这让我想到了一个问题,即Android或Cocos2d-x在异步执行某些操作(更新Achievement)或结合使用NDK更改"活动"(我使用NDKr7,但在NDKr5上也是如此)时存在一些问题。
您还应该知道,我已经在Java中定义了一些其他函数,这些函数是用JNI调用调用的,并且可以正常工作!
也许我做错了什么,有人能给我一些关于这方面的建议吗?或者给我一个如何更改活动的工作示例代码。也许是Cocos2d-x的问题。
谢谢。
我不知道Dalvik的实现,但@Goz是对的:JNIEnv指针的作用域只是您调用的JNI函数的持续时间。如果你想从本机代码回调到Java,你不能只保存上一次调用的JNIEnv,因为那个调用可能不再有效(因此appcrash)。如果你每件事都只有一个线程,那么它就会起作用。
您需要做的(如果您有多个线程)是在每次回调时获得一个有效的JNIEnv指针。在初始化功能中,您保存一个指向当前运行的虚拟机的指针:
JavaVM *jvm;
env->GetJavaVM(&jvm);
然后,您可以使用对正在运行的虚拟机的引用,通过调用来获得有效的JNIEnv指针
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
然后你就可以使用env了,当你完成后,别忘了打电话给
jvm->DetachCurrentThread();
附加/分离将导致Java向thread对象注册调用方(可以是本机线程),从而将其视为Java线程。
免责声明:至少,这是你在桌面Java中的做法。不知道Dalvik的实现,但从外观上看,他们只是复制了技术。
我已经找到了我的案例的答案。它很容易修复,但很难找到。当应用程序更改为新活动时,将调用来自cocos2d-x MessageJNI的nativeOnPause方法。这个方法应该调用CCApplication::sharedApplication(),但我的一个类以前调用过CCApplication析构函数,它将共享的singleton清除为null。
编辑:这是对原帖子的完整编辑,所以评论不再有意义了。
- 当回溯以零开始时,如何调试崩溃
- 内联映射初始化的动态atexit析构函数崩溃
- 执行函数时导致崩溃的变量
- 程序崩溃并显示"std::out_of_range"错误
- CoInitialize()在单独的线程上崩溃而不返回
- 使用调试/崩溃报告将应用程序部署到客户端
- 为什么所有C++编译器都会崩溃或挂起此代码
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 为什么我的多线程作业队列崩溃
- ExtractIconEx:可以工作,但偶尔会崩溃
- 为什么引用传递会导致此崩溃(C++)
- 试图创建流或fopen时程序崩溃
- 类对象数组的问题会导致崩溃
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 为什么要增加导致崩溃的指针
- 在虚幻引擎中删除NXOpen对象时崩溃
- 为什么它只打印双链接列表的第一个值,而我的程序却崩溃了
- 应用程序崩溃并显示"symbol _ZdlPvm, version Qt_5 not defined in file libQt5Core.so.5 with link time reference"
- 如果 DEP 处于活动状态,则迁移的应用程序崩溃
- 使用JNI调用或使用Openfeint更改活动会导致应用程序崩溃