Вызов члена класса JAVA из собственного кода C / C ++ - PullRequest
6 голосов
/ 01 сентября 2011

Я пишу приложение OpenGL C / C ++, которое я портирую на Android через Android NDK, поддержку JNI. У меня проблемы с выполнением кода из обратного вызова 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!!!");
}

Результат… с нативной стороны я получаю класс, я получаю метод, метод вызывается, но когда он достигает / вызывает requestRender () внутри (или пытается получить доступ к любому другому методу-члену GLSurfaceView, он падает!)

Я не могу попробовать с CallStaticVoidMethod (gJClass, gjMethodID); потому что тогда у меня нет доступа к requestRender ();

Любые идеи или мнения, может быть, я здесь что-то не так делаю.

Спасибо

1 Ответ

3 голосов
/ 01 сентября 2011

Вам необходимо создать глобальные ссылки на класс / объект, который вы спрятали.Сохраняемые ссылки: локальные ссылки , которые нельзя разделить между потоками и исчезают, когда среда выполнения очищает локальный стек ссылок JNI.

Проверьте Sun/ Документация Oracle для глобальных и локальных ссылок и проверки методов JNI JNIEnv::NewGlobalRef и JNIEnv::DeleteGlobalRef.

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

gJObjectCached = env->NewGlobalRef(obj);

(Изменить: Оказывается, вам не нужны глобальные ссылки для идентификаторов методов.)

...