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 .

  • Llamar a DeleteLocalRef en la interfaz nativa de java
  • Android OpenGL ES: auto-corrección env-> self y NvRmChannelSubmit fallaron
  • Llame a métodos de clase C ++ o funcione desde Java en android sin recrear clase / variable en cada llamada
  • Copiar un buffer de bytes con JNI
  • ¿Cómo funciona NDK en Android - ¿Cuál es la orden que NDK, JNI etc se utilizan?
  • Cómo crear jni y Android.mk?
  • Privilegios raíz para una aplicación Android (aplicación completa, no sólo unos pocos comandos)
  • Android NDK y C + + STL
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.