2011-09-01 66 views
6

我正在编写一个OpenGL C/C++应用程序,通过Android NDK,JNI支持将其移植到Android。从本机发出的JAVA回调中执行代码时遇到困难。从本地C/C++代码调用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(…); 
    } 
} 

而且在本机代码我有一个功能texturesLoaded()时,一些纹理在另一个线程完全加载信号通知,我需要强制从nativeLib.draw刷新(......)在JAVA端。这是我如何做到这一点:

我缓存在JavaVM,JCLASS,jMethodID上JNI_OnLoad和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; 
... 
} 

,然后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!!!"); 
} 

结果...从本机端我得到类,我得到的方法,方法被调用,但是当它达到/ cal ls requestRender()里面(或试图访问任何其他成员方法的GLSurfaceView它崩溃!)

我不能尝试使用CallStaticVoidMethod(gJClass,gjMethodID);因为那样我就无法访问requestRender();

任何想法或意见,也许我在这里做错了什么。

感谢

+0

'requestRender()'是Java,所以崩溃通常都伴随着一个例外。你能提一下那个例外吗? Android ADB日志应该显示这些信息。去[这里](http://developer.android.com/guide/developing/debugging/debugging-log.html)找出关于日志 – Warpspace

回答

3

您需要创建全局引用的类/对象,你藏了。您保存的引用是本地引用,它们不能跨线程共享,并且在运行时清除JNI本地引用堆栈时消失。

查看Sun/Oracle documentation for global and local references,并查看JNI方法JNIEnv::NewGlobalRefJNIEnv::DeleteGlobalRef

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

gJObjectCached = env->NewGlobalRef(obj); 

(编辑:原来你并不需要的方法ID全局引用。)

+0

你的答案是非常有用的,但它并没有帮助我的问题。我的全局缓存jclass和jmethodID是有效的,我可以达到我的JAVA回调,这是该类(它不是静态的)的成员。问题是我不能从该回调中调用该类的另一个成员,因为我认为我缓存的jobject不指向:“com/android/newlineactivity/NewLineGLSurfaceView”,因为我可能将其缓存在另一个JENV中。所以对于一个静态成员这个工作:env-> CallStaticVoidMethod(gJClass,gJMethodID);但对于非静态的,这不包含:env-> CallVoidMethod(gJObjectCached,gJMethodID);有意义吗? – eKKo82

+0

'gJObjectCached'是一个全局引用,还是只存储了传递给'cachejavaobject'的本地引用? –

+0

它是一个全局引用,在我调用env-> CallVoidMethod(gJObjectCached,gJMethodID)时有效;但是,当它在内部调用requestRender()时,该回调()会崩溃。我什至尝试jobject jobj = env-> NewObject(gJClass,gJMethodID);并通过它,希望用正确的env,gjClass和gJMethodID我可以调用该方法,它会工作...方法被调用,但在requestRender()再次崩溃; – eKKo82