Android: Uso de JNI desde NativeActivity

Estamos desarrollando un juego OpenGL en android usando la clase NativeActivity . Hasta ahora todo ha ido bien, pero ahora tenemos que acceder a algunas funcionalidades que sólo parece estar disponible en Java.

Hay más, pero el primero que pensamos que sería útil fue acceder a la pantalla DPI. Como se describe aquí, el código Java se ve así:

DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); 

Y aquí está el desafortunado código C ++ correspondiente:

 // My checking routine. #define JNI_ASSERT(jni, cond) { \ if (!(cond)) {\ std::stringstream ss; \ ss << __FILE__ << ":" << __LINE__; \ throw std::runtime_error(ss.str()); \ } \ if (jni->ExceptionCheck()) { \ std::stringstream ss; \ ss << __FILE__ << ":" << __LINE__; \ throw std::runtime_error("Exception: " + ss.str()); \ } \ } void print_dpi(android_app* app) { JNIEnv* jni; app->activity->vm->AttachCurrentThread(&jni, NULL); jclass activityClass = jni->FindClass("android/app/NativeActivity"); JNI_ASSERT(jni, activityClass); jmethodID getWindowManager = jni->GetMethodID ( activityClass , "getWindowManager" , "()Landroid/view/WindowManager;"); JNI_ASSERT(jni, getWindowManager); jobject wm = jni->CallObjectMethod(app->activity->clazz, getWindowManager); JNI_ASSERT(jni, wm); jclass windowManagerClass = jni->FindClass("android/view/WindowManager"); JNI_ASSERT(jni, windowManagerClass); jmethodID getDefaultDisplay = jni->GetMethodID( windowManagerClass , "getDefaultDisplay" , "()Landroid/view/Display;"); JNI_ASSERT(jni, getDefaultDisplay); jobject display = jni->CallObjectMethod(wm, getDefaultDisplay); JNI_ASSERT(jni, display); jclass displayClass = jni->FindClass("android/view/Display"); JNI_ASSERT(jni, displayClass); // Check if everything is OK so far, it is, the values it prints // are sensible. { jmethodID getWidth = jni->GetMethodID(displayClass, "getWidth", "()I"); JNI_ASSERT(jni, getWidth); jmethodID getHeight = jni->GetMethodID(displayClass, "getHeight", "()I"); JNI_ASSERT(jni, getHeight); int width = jni->CallIntMethod(display, getWidth); JNI_ASSERT(jni, true); log("Width: ", width); // Width: 320 int height = jni->CallIntMethod(display, getHeight); JNI_ASSERT(jni, true); log("Height: ", height); // Height: 480 } jclass displayMetricsClass = jni->FindClass("android/util/DisplayMetrics"); JNI_ASSERT(jni, displayMetricsClass); jmethodID displayMetricsConstructor = jni->GetMethodID( displayMetricsClass , "<init>", "()V"); JNI_ASSERT(jni, displayMetricsConstructor); jobject displayMetrics = jni->NewObject( displayMetricsClass , displayMetricsConstructor); JNI_ASSERT(jni, displayMetrics); jmethodID getMetrics = jni->GetMethodID( displayClass , "getMetrics" , "(Landroid/util/DisplayMetrics;)V"); JNI_ASSERT(jni, getMetrics); jni->CallVoidMethod(display, getMetrics, displayMetrics); JNI_ASSERT(jni, true); { jfieldID xdpi_id = jni->GetFieldID(displayMetricsClass, "xdpi", "F"); JNI_ASSERT(jni, xdpi_id); float xdpi = jni->GetFloatField(displayMetricsClass, xdpi_id); JNI_ASSERT(jni, true); log("XDPI: ", xdpi); // XDPI: 0 } { jfieldID height_id = jni->GetFieldID( displayMetricsClass , "heightPixels", "I"); JNI_ASSERT(jni, height_id); int height = jni->GetIntField(displayMetricsClass, height_id); JNI_ASSERT(jni, true); log("Height: ", height); // Height: 0 } // TODO: Delete objects here. app->activity->vm->DetachCurrentThread(); } 

El código emite:

 Width: 320 Height: 480 XDPI: 0 Height: 0 

Es como si el objeto displayMetrics no se estableció en la llamada

 jni->CallVoidMethod(display, getMetrics, displayMetrics); 

¿Es el caso de que JNI no me permitiera usar un argumento como un valor de retorno? Si es así, ¿cómo podemos darle la vuelta dado que estamos usando el pegamento de NativeActivity.

2 Solutions collect form web for “Android: Uso de JNI desde NativeActivity”

Ech, he estado mirando el código durante un par de horas y no lo veo. Luego dejó el escritorio, volvió y allí estaba:

En lugar de estas dos líneas

 float xdpi = jni->GetFloatField(displayMetricsClass, xdpi_id); int height = jni->GetIntField(displayMetricsClass, height_id); 

Debería haber usado:

 float xdpi = jni->GetFloatField(displayMetrics, xdpi_id); int height = jni->GetIntField(displayMetrics, height_id); 

Doh

(Al menos puede servir como un ejemplo si alguien quiere conseguir DPI de la manera difícil :))

En primer lugar, debo señalar que no soy muy bueno con JNI 🙂

Dicho esto, sospecho que el problema sería que la variable displayMetrics debe convertirse en una referencia global con

 displayMetrics = jni->NewGlobalRef(displayMetrics); 

o algo así. Recuerde que debe unref con DeleteGlobalRef . LocalRef podría funcionar también …

Pero si tuviera que ir a resolver este problema, lo envolvería todo en una función java y simplemente llamaría desde nativo y dejaría que el lado java hiciera la mayor parte de la función llamando – también hay menos saltos de la valla java-nativa involucrada .

  • Androide ndk multitouch
  • Newbie en Android NDK: definición en Android.mk
  • La aplicación se bloquea al cambiar de actividad de cocos2d-x a otra actividad en Android
  • Cómo especificar el archivo de encabezado c en el archivo android.mk
  • Llamar a un objeto java guardado a través de JNI desde un hilo diferente
  • ¿Cómo obtener los mensajes "printf" escritos en la aplicación NDK?
  • Cómo utilizar la API de malla experimental con Project Tango
  • ¿Puedes detectar un error de JNI en una biblioteca de terceros antes de un duro choque?
  • Cómo convertir char a jstring en JNI?
  • ¿Cómo crear una biblioteca nativa de C ++ en Android?
  • Cómo utilizar OpenSSL Library en la aplicación ANDROID
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.