我正在编写一个 OpenGL C/C++ 应用程序,我正在通过 Android NDK、JNI 支持将其移植到 Android。我在从 native 发出的 JAVA 回调中执行代码时遇到困难。
代码如下:
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(…);
}
}
在 native 代码中,我有一个函数 texturesLoaded(),当某些纹理在另一个线程上完全加载时发出信号,我需要在 JAVA 端从 nativeLib.draw(…) 强制刷新。这是我的做法:
我在 JNI_OnLoad 和 gJObjectCached 上缓存 JavaVM、jClass、jMethodID
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;
...
}
然后我在 texturesLoaded() 上做:
void texturesLoaded()
{
// Cannot share a JNIEnv between threads. You should share the JavaVM, and use JavaVM->GetEnv to discover the thread's 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!!!");
}
结果…从 native 端我得到了类,我得到了方法,方法被调用,但是当它到达/调用内部的 requestRender() 时(或试图访问 GLSurfaceView 的任何其他成员方法它崩溃了!)
我无法尝试使用 CallStaticVoidMethod(gJClass, gjMethodID);因为那时我无权访问 requestRender();
任何想法或意见,也许我在这里做错了什么。
谢谢
最佳答案
您需要为您隐藏的类/对象创建全局引用。您正在保存的引用是本地引用,它不能跨线程共享,并且会在运行时清理 JNI 本地引用堆栈时消失。
查看 Sun/Oracle documentation for global and local references ,并查看 JNI 方法 JNIEnv::NewGlobalRef
和 JNIEnv::DeleteGlobalRef
.
gJClass = env->NewGlobalRef(env->FindClass( ... ));
gJObjectCached = env->NewGlobalRef(obj);
(编辑:事实证明您不需要方法 ID 的全局引用。)
关于android - 从 native C/C++ 代码调用 JAVA 类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7268450/