2012-03-08 13 views
5

Tengo un gran problema cuando quiero cambiar la actividad de mi Android-aplicación con una llamada JNI de mi código C++. La aplicación usa cocos2d-x para renderizar. La situación concreta es que quiero abrir el OpenFeint-tablero de instrumentos en Java utilizando esta pequeña función:cambia su actividad con JNI llamadas o usando Openfeint causa de App-Crash

void launchOpenFeintDashboard() { 
    Dashboard.open(); 
} 

Esta función se llama luego desde C++ con un simple JNI-Call:

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

la aplicación JNIManager clase es también muy simple y básica:

#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; 
} 

Desde mi punto de vista, cocos2d-x tiene algunos problemas cuando se cambia el cirical actividad con una llamada JNI, porque también obtengo un bloqueo de aplicación al cambiar la actividad a cualquier actividad propia.

PERO, también cuando simplemente uso OpenFeint para actualizar un logro con una JNI llaman consigo una App-colisión, similar a cuando se cambia la Actividad:

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

Esto me lleva a un punto en lo que Diría que Android o Cocos2d-x tienen algún problema al hacer algo asincrónicamente (actualizar Logro) o al cambiar la Actividad en combinación con el uso del NDK (yo uso NDKr7, pero igual en NDKr5).

También debe saber que ya tengo algunas otras funciones definidas en Java que se denominan con una llamada JNI, y que funcione correctamente!

Tal vez he hecho algo mal, ¿alguien me puede dar alguna información sobre este o un código de muestra de trabajo sobre cómo cambiar la actividad? Tal vez es un problema con Cocos2d-x.

Gracias.

+2

AFAIK el env JNI solo es válido mientras la llamada de función esté activa ... Tampoco se puede garantizar que el objeto siga siendo válido. Básicamente, necesitamos ver cómo terminas en la llamada al launchDashboard. es decir, desde la entrada inicial al nativo de java ... – Goz

+0

¿Por qué tienes más de una 'Actividad'? ¿Has revisado este tutorial ?: http://blog.molioapp.com/2011/11/openfeint-and-admob-integrated-with.html – Macarse

Respuesta

3

he encontrado la respuesta a mi caso. Fue fácil de arreglar, pero complejo de encontrar. Cuando la aplicación cambia a una nueva actividad, se llama al método nativeOnPause de cocos2d-x MessageJNI. Se supone que este método llama a CCApplication :: sharedApplication(), pero una de mis clases había llamado previamente al destructor CCApplication, que quitó el singleton compartido a nulo.

EDIT: Esta es una edición completa de la publicación original, por lo que los comentarios no hacen sentido.

+0

@Goz Quizás, pero el código que proporcionaste aún tiene errores. Está guardando un puntero JNIEnv en su clase JNIManager y luego usando este puntero en el método launchDashboard(). Si ese método es invocado por otro subproceso o incluso por otra función JNI, obtendrás el appcrash. Aunque el problema parece estar relacionado con Cocos o OpenFeint, usted (supongo) necesita inicializar estas bibliotecas con algunas estructuras JNI (puntero JNIEnv, tal vez). Si pasa un puntero no válido, la aplicación se bloqueará dentro de la biblioteca. –

+1

No soy yo en la pregunta. La implementación de mi código es diferente. –

+0

, entonces tal vez podrías publicar el código con el que realmente tienes un problema. –

5

No sé sobre la implementación de Dalvik, pero @Goz tiene razón: el alcance para el puntero JNIEnv es solo para la duración de la función JNI que llame. Si desea realizar una devolución de llamada desde el código nativo a Java, no puede simplemente guardar JNIEnv de la llamada anterior, porque es posible que ya no sea válida (de ahí que se ejecute la aplicación). Si tuviera un solo hilo haciendo cada cosa, entonces funcionaría.

Lo que necesita hacer (si tiene múltiples hilos), es obtener un puntero JNIEnv válido cada vez que vaya a devolver la llamada.En la función de inicialización, se guarda un puntero a la corriente de la máquina virtual en ejecución:

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

A continuación, puede utilizar esta referencia a una máquina virtual que se ejecuta para obtener un puntero JNIEnv válida llamando:

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

a continuación, puede trabajar con el env y cuando haya terminado, no olvide llamar

jvm->DetachCurrentThread(); 

la acopla/desacopla causará Java para registrar la persona que llama (que puede ser un na tivo) con un objeto Thread, que le permite ser tratado como un hilo Java.

DESCARGO DE RESPONSABILIDAD: Al menos, así es como lo hace en el escritorio de Java. No sé sobre la implementación de Dalvik, pero por lo que parece, simplemente copiaron la tecnología.

+1

Estoy bastante seguro de que lo que dices es correcto para Android también ... ¿No hay una manera? para sostener un objeto (es decir, evitar que obtenga GC'd de otro hilo también) – Goz

Cuestiones relacionadas