Calling a member of a JAVA class from Native C / C ++ code

I am writing an OpenGL C / C ++ application that I port to Android via Android NDK, JNI support. I'm having difficulty executing code from a JAVA callback passed using native.

Here is the code:

class OpenGLSurfaceView extends GLSurfaceView { … public OpenGLSurfaceView(Context context, int deviceWidth, int deviceHeight) { super(context); nativeObj = new NativeLib(); mRenderer = new OpenGLRenderer(context, nativeObj, deviceWidth, deviceHeight); setRenderer(mRenderer); setRenderMode(RENDERMODE_WHEN_DIRTY); } … private void CallBack() { // force redraw requestRender(); } } class OpenGLRenderer implements GLSurfaceView.Renderer { … public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeObj.init(…); nativeObj.cachejavaobject(JNIEnv *env, jobject obj); // for caching obj on native side } public void onSurfaceChanged(GL10 gl, int w, int h) { } public void onDrawFrame(GL10 gl) { nativeObj.draw(…); } } 

And in my own code, I have a texturesLoaded () function that is signaled when some textures are loaded completely into another stream, and I need to force update from nativeLib.draw (...) on the JAVA side. Here is how I do it:

I cache JavaVM, jClass, jMethodID in JNI_OnLoad and gJObjectCached

 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { gJVM = jvm; // cache the JavaVM pointer LogNativeToAndroidExt("JNILOAD!!!!!!!!!!"); JNIEnv *env; int status = gJVM->GetEnv((void **)&env, JNI_VERSION_1_6); if(status < 0) { LogNativeToAndroidExt("Failed to get JNI environment, assuming native thread"); status = gJVM->AttachCurrentThread(&env, NULL); if(status < 0) { LogNativeToAndroidExt("callback_handler: failed to attach current thread"); return JNI_ERR; } } gJClass = env->FindClass("com/android/newlineactivity/NewLineGLSurfaceView"); if(gJClass == NULL) { LogNativeToAndroidExt("Can't find Java class."); return JNI_ERR; } gJMethodID = env->GetMethodID(gJClass, "callback", "()V"); if(gJMethodID == NULL) { LogNativeToAndroidExt("Can't find Java method void callback()"); return JNI_ERR; } return JNI_VERSION_1_6; 

}

 JNIEXPORT void Java_com_android_OpenGL_NativeLib_cachejavaobject(JNIEnv* env, jobject obj) { // cache the java object gJObjectCached = obj; ... } 

and then on texturesLoaded () I do:

 void texturesLoaded() { // Cannot share a JNIEnv between threads. You should share the JavaVM, and use JavaVM->GetEnv to discover the thread JNIEnv JNIEnv *env = NULL; int status = gJVM->GetEnv((void **)&env, JNI_VERSION_1_6); if(status < 0) { LogNativeToAndroidExt("callback_handler: failed to get JNI environment, assuming native thread"); status = gJVM->AttachCurrentThread(&env, NULL); if(status < 0) { LogNativeToAndroidExt("callback_handler: failed to attach current thread"); return; } } LogNativeToAndroidExt("Calling JAVA method from NATIVE C/C++"); env->CallVoidMethod(gJObjectCached, gJMethodID); LogNativeToAndroidExt("DONE!!!"); } 

The result ... on the native side, I get the class, I get the method, the method is called, but when it reaches / calls requestRender () inside (or tries to access any other method of the GLSurfaceView member, it will work!)

I can not try with CallStaticVoidMethod (gJClass, gjMethodID); because then I do not have access to requestRender ();

Any ideas or opinions, maybe I'm doing something wrong here.

thanks

+6
source share
1 answer

You need to create global references to the class / object that you are dropping. The references you save are local links that cannot be shared between threads and disappear when the runtime clears the local JNI control stack.

Check out the Sun / Oracle documentation for global and local references and check out the JNI methods JNIEnv::NewGlobalRef and JNIEnv::DeleteGlobalRef .

 gJClass = env->NewGlobalRef(env->FindClass( ... )); gJObjectCached = env->NewGlobalRef(obj); 

(Edit: Turns out you don't need global references for method identifiers.)

+3
source

Source: https://habr.com/ru/post/896390/


All Articles