And*_*ann 5 c++ java-native-interface android android-ndk cocos2d-x
当我想用我的C++代码中的JNI调用来改变我的Android-Application的Activity时,我遇到了很大的问题.该应用程序使用cocos2d-x进行渲染.具体情况是我想使用这个非常小的函数在Java中打开OpenFeint-Dashboard:
void launchOpenFeintDashboard() {
Dashboard.open();
}
Run Code Online (Sandbox Code Playgroud)
然后使用简单的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");
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
从我的角度来看,cocos2d-x在使用JNI调用更改活动时会出现一些循环问题,因为在将Activity更改为任何自己的Activity时,我也会遇到App-Crash.
但是,当我只是使用OpenFeint通过JNI调用更新成就时,我得到了一个App-Crash,类似于更改Activity时:
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() + ")");
}
Run Code Online (Sandbox Code Playgroud)
这让我想到了我要说的一点,即Android或Cocos2d-x在执行异步(更新成就)时或者在使用NDK时更改Activity(我使用NDKr7,但在NDKr5上相同)时会出现问题.
您还应该知道我已经在Java中定义了一些其他函数,这些函数通过JNI调用调用并且正常工作!
也许我做错了什么,有人可以给我一些建议或一个如何改变活动的工作示例代码.也许这是Cocos2d-x的一个问题.
谢谢.
我不知道Dalvik实现,但@Goz是正确的:JNIEnv指针的范围仅适用于您调用的JNI函数的持续时间.如果您想从本地代码到Java做回调,你不能只是保存的JNIEnv从以前的电话,因为人们可能不再有效(因此appcrashes).如果你只有一个线程在做每个人,那么它会起作用.
你需要做的(如果你有多个线程),是每次你要回调时获得一个有效的JNIEnv指针.在初始化函数中,您将指针保存到当前正在运行的虚拟机:
JavaVM *jvm;
env->GetJavaVM(&jvm);
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过调用以下方法将此引用用于正在运行的虚拟机以获取有效的JNIEnv指针:
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
Run Code Online (Sandbox Code Playgroud)
然后你可以使用env,当你完成后,不要忘记打电话
jvm->DetachCurrentThread();
Run Code Online (Sandbox Code Playgroud)
附加/分离将导致Java使用Thread对象注册调用者(可以是本机线程),这允许将其视为Java线程.
免责声明:至少,这是你在桌面Java中的表现.不知道Dalvik的实现,但从外观来看,他们只是复制了这项技术.
我已经找到了我的案例的答案。修复起来很容易,但找到起来却很复杂。当应用程序更改为新活动时,会调用 cocos2d-x MessageJNI 中的 nativeOnPause 方法。该方法应该调用 CCApplication::sharedApplication(),但我的一个类之前调用了 CCApplication 析构函数,该析构函数将共享单例清除为 null。
编辑:这是对原始帖子的完整编辑,因此评论不再有意义。