2011-09-01 27 views
6

Tôi đang viết một ứng dụng OpenGL C/C++ mà tôi đang chuyển sang Android thông qua hỗ trợ Android NDK, JNI. Tôi gặp khó khăn khi thực thi mã từ cuộc gọi lại JAVA được báo hiệu từ gốc.Gọi cho thành viên của lớp JAVA từ mã Native C/C++

Đây là mã:

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

Và trong mã nguồn gốc tôi có một chức năng texturesLoaded() mà là báo hiệu khi một số kết cấu được nạp hoàn toàn vào thread khác và tôi cần phải buộc một refresh từ nativeLib.draw (…) Về phía JAVA. Dưới đây là làm thế nào tôi làm điều đó:

Tôi cache JavaVM, jClass, jMethodID trên JNI_OnLoad, và 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; 
... 
} 

và sau đó trên texturesLoaded() i làm:

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

Kết quả… từ bên gốc tôi có được lớp, tôi nhận được phương pháp, phương pháp được gọi, nhưng khi đạt đến/cal l requestRender() bên trong (hoặc cố gắng truy cập bất kỳ phương pháp thành viên nào khác của GLSurfaceView nó bị treo!)

Tôi không thể thử với CallStaticVoidMethod (gJClass, gjMethodID); bởi vì sau đó tôi không có quyền truy cập vào requestRender();

Bất kỳ ý tưởng hoặc ý kiến ​​nào, có thể tôi đang làm điều gì đó sai ở đây.

Cảm ơn

+0

'requestRender() 'là trong Java, vì vậy tai nạn thường được đi kèm với một ngoại lệ. Bạn có thể đề cập đến ngoại lệ đó là gì không. Nhật ký của Android ADB sẽ tiết lộ thông tin đó. Truy cập [Here] (http://developer.android.com/guide/developing/debugging/debugging-log.html) để tìm hiểu về nhật ký – Warpspace

Trả lời

3

Bạn cần phải tạo tài liệu tham khảo toàn cầu đến lớp/đối tượng mà bạn giấu đi. Các tham chiếu bạn đang lưu là tham chiếu cục bộ, không thể chia sẻ giữa các chuỗi và biến mất khi thời gian chạy dọn sạch ngăn xếp tham chiếu cục bộ JNI.

Khám phá Sun/Oracle documentation for global and local references và xem các phương pháp JNI JNIEnv::NewGlobalRefJNIEnv::DeleteGlobalRef.

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

gJObjectCached = env->NewGlobalRef(obj); 

(Edit: Hóa ra bạn không cần phải tham khảo toàn cầu cho các ID phương pháp.)

+0

Câu trả lời của bạn rất hữu ích, nhưng nó không giúp được vấn đề của tôi. JclasshodID lưu trữ toàn cục của tôi là hợp lệ, tôi có thể tiếp cận gọi lại JAVA của tôi, là một thành viên của lớp đó (không phải tĩnh của nó). Vấn đề là tôi không thể gọi một thành viên khác của lớp đó từ cuộc gọi lại đó, bởi vì tôi nghĩ bộ nhớ cache jobject i không trỏ tới: "com/android/newlineactivity/NewLineGLSurfaceView" vì tôi có thể lưu nó vào một JENV khác. Vì vậy, đối với một thành viên tĩnh này hoạt động: env-> CallStaticVoidMethod (gJClass, gJMethodID); nhưng đối với một không tĩnh này không: env-> CallVoidMethod (gJObjectCached, gJMethodID); – eKKo82

+0

Có phải 'gJObjectCached' là tham chiếu toàn cầu hay bạn vừa lưu trữ tham chiếu cục bộ được chuyển vào' cachejavaobject'? –

+0

Tham chiếu toàn cầu của nó và hợp lệ tại thời điểm tôi gọi env-> CallVoidMethod (gJObjectCached, gJMethodID); Nhưng callback đó() bị treo khi nó gọi requestRender() trong nội bộ.Tôi thậm chí đã thử jobject jobj = env-> NewObject (gJClass, gJMethodID); và vượt qua điều đó, với hy vọng rằng với đúng env, gjClass và gJMethodID tôi có thể gọi phương thức và nó sẽ hoạt động ... các phương thức được gọi nhưng lại bị treo tại requestRender(); – eKKo82

Các vấn đề liên quan