2012-03-08 2 views
5

私のAndroidアプリケーションのアクティビティをC++コードからのJNI呼び出しで変更したいときに大きな問題があります。アプリはcocos2d-xをレンダリングに使用します。 具体的な状況は、私はこの非常に小さな関数を使用してJavaでOpenFeintを、ダッシュボードを開くようにしたいということです。JNIコールでアクティビティを変更するかOpenfeintを使用するとアプリケーションクラッシュが発生する

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は、いくつかの問題を抱えているciricalアクティビティを自分のアクティビティに変更するときにもApp-Crashが発生するため、JNIコールでアクティビティを呼び出すことができます。

しかし、また、私は単にJNIとの達成を更新するためにOpenFeintをを使用する場合、私は活動を変更する場合と同様のApp-クラッシュを取得呼び出します

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() + ")"); 
} 

これは、どのようなI上のポイントに私をもたらしAndroidやCocos2d-xは、非同期的に何かをする(Achievementを更新する)ときや、NDKrを使う(NDKr7を使うが、NDKr5でも同じ)と組み合わせてActivityを変更するときに問題がある。

また、Javaで定義されたいくつかの関数がJNI呼び出しで呼び出され、正しく動作することも知っておく必要があります。

私は何か間違ったことをしたかもしれませんが、 誰かがこのことや活動を変更するための働くサンプルコードを教えてくれますか?おそらく、Cocos2d-xには問題があります。

ありがとうございました。

+2

AFAIK JNI envは、関数呼び出しがアクティブである限り有効です。オブジェクトがまだ有効であることを保証することもできません。基本的には、launchDashboardコールでどのように終わるかを確認する必要があります。つまり、最初のエントリからJavaのネイティブへ... – Goz

+0

なぜあなたは複数の 'Activity'を持っていますか?このチュートリアルをチェックしましたか:http://blog.molioapp.com/2011/11/openfeint-and-admob-integrated-with.html – Macarse

答えて

3

私の場合の回答が見つかりました。修正するのは簡単でしたが、見つけるのは複雑でした。アプリケーションが新しいアクティビティに変更されると、cocos2d-x MessageJNIのnativeOnPauseメソッドが呼び出されます。このメソッドはCCApplication :: sharedApplication()を呼び出すことになっていますが、私のクラスの1つは以前に共有シングルトンをnullにクリアしたCCApplicationデストラクタを呼び出していました。

EDIT:これは元の投稿の完全な編集であり、コメントは意味をなさない。

+0

@Gozおそらく、あなたが提供したコードはまだバグです。 JNIannvポインターをJNIManagerクラスに保存してから、このポインターをlaunchDashboard()メソッドで使用しています。そのメソッドが別のスレッドまたは別のJNI関数によって呼び出された場合、appcrashが返されます。問題はココスやOpenFeintに関係しているようですが、おそらくJNI構造(JNIEnvポインタ、おそらく)でこれらのライブラリを初期化する必要があります。無効なポインタを渡すと、アプリケーションはライブラリ内でクラッシュします。 –

+1

質問で私ではありません。私のコードの実装は異なります。 –

+0

あなたは実際に問題があるコードを投稿することができます。 –

5

Dalvikの実装についてはわかりませんが、@ Gozは正しいです:JNIEnvポインタのスコープは、あなたが呼び出したJNI関数の期間だけです。ネイティブコードからJavaへのコールバックを行いたい場合は、JNIEnvが有効ではなくなる可能性があるため、前の呼び出しからJNIEnvを保存するだけではありません(したがってappcrashes)。毎回スレッドを1つしか持たない場合、それはうまくいくでしょう。

複数のスレッドがある場合は、コールバックするたびに有効なJNIEnvポインタを取得する必要があります。呼び出すことにより、あなたは、有効なJNIEnvのポインタを取得するために、実行中の仮想マシンにこのリファレンスを使用することができます

JavaVM *jvm; 
env->GetJavaVM(&jvm); 

:初期化機能では、現在実行中の仮想マシンへのポインタを保存

JNIEnv *env; 
jvm->AttachCurrentThread((void **)&env, NULL); 

その後、ENVで作業ができ、作業が終了したときに、/デタッチを添付

jvm->DetachCurrentThread(); 

を呼び出すことを忘れないでくださいJavaはNAことができ、発信者が(登録されますスレッドオブジェクトをJavaオブジェクトとして扱うことができます。

免責事項:少なくとも、これはデスクトップJavaで行う方法です。 Dalvikの実装についてはわかりませんが、その外観からは技術をコピーしただけです。

+1

私はあなたの言うことがアンドロイドにとっても正しいと確信しています...方法はありませんオブジェクトを保持する(つまり、別のスレッドからGCを取得しないようにする) – Goz

関連する問題