Android: using JNI from NativeActivity

We are developing an OpenGL game on android using the NativeActivity class. So far, everything went well, but now we need to access some functions that seem to be available only in Java.

There is more, but the first, which, in our opinion, would be useful, is access to the DPI display. As described here , the Java code is as follows:

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

And here is the failed corresponding C ++ code:

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

Code Outputs:

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

As if the displayMetrics object was not set in the call

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

Is this the case when the JNI did not allow me to use the argument as a return value? If so, how can we get around this, given that we use NativeActivity glue.

+7
source share
2 answers

Eh, I looked at the code for a couple of hours and did not see it. Then he left the table, returned, and here he is:

Instead of these two lines

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

I should have used:

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

Doh :)

(at least this can serve as an example if someone wants to get DPI in the hard way :))

+5
source

First of all, I should note that I'm not very good at JNI :)

However, I suspect the problem would be that the displayMetrics variable should be made a global reference using

 displayMetrics = jni->NewGlobalRef(displayMetrics); 

or something like that. Remember to cancel it with DeleteGlobalRef . LocalRef can work too ...

But if I decided to solve this problem, I would wrap it all in a java function and just call it from native, and let the java side perform most of the function call - there also jumps less java native fence.

+1
source

All Articles