Android 从本机C/C+;调用JAVA类成员+;代码

Android 从本机C/C+;调用JAVA类成员+;代码,android,java-native-interface,android-ndk,Android,Java Native Interface,Android Ndk,我正在编写一个OpenGL C/C++应用程序,通过Android NDK和JNI支持将其移植到Android。我在从本机发出的JAVA回调执行代码时遇到困难 代码如下: class OpenGLSurfaceView extends GLSurfaceView { … public OpenGLSurfaceView(Context context, int deviceWidth, int deviceHeight) { super(context);

我正在编写一个OpenGL C/C++应用程序,通过Android NDK和JNI支持将其移植到Android。我在从本机发出的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(),当一些纹理完全加载到另一个线程上时,会发出信号,我需要在JAVA端强制从nativeLib.draw(…)刷新。我是这样做的:

我在JNI_OnLoad上缓存JavaVM、jClass、jMethodID和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;
然后在texturesLoaded()上,我执行以下操作:

void texturesLoaded()
{
//无法在线程之间共享JNIEnv。您应该共享JavaVM,并使用JavaVM->GetEnv来发现线程的JNIEnv
JNIEnv*env=NULL;
int status=gJVM->GetEnv((void**)和env,JNI\u版本1\u 6);
如果(状态<0)
{
LogNativeToAndroText(“回调_处理程序:无法获取JNI环境,假设为本机线程”);
status=gJVM->AttachCurrentThread(&env,NULL);
如果(状态<0)
{
LogNativeToAndroText(“回调处理程序:未能附加当前线程”);
返回;
}
}
LogNativeToAndroText(“从本机C/C++调用JAVA方法”);
env->CallVoidMethod(gJObjectCached,gJMethodID);
LognativeToAndroText(“完成!!!”);
}
结果…从本机端我得到了类,得到了方法,调用了方法,但当它到达/调用内部的requestRender()时(或试图访问GLSurfaceView的任何其他成员方法时,它崩溃了!)

我无法尝试使用CallStaticVoidMethod(gJClass,gjMethodID);因为这样我就没有访问requestRender()的权限

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

谢谢

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

检查,并检查JNI方法和

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

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

requestRender()是Java语言,所以崩溃通常伴随着异常。你能说说那个例外是什么吗。Android ADB日志应该会显示这些信息。你的答案很有用,但对我的问题没有帮助。我的全局缓存jclass和jmethodID是有效的,我可以访问我的JAVA回调,它是该类的一个成员(不是静态的)。问题是我无法从该回调调用该类的另一个成员,因为我认为我缓存的jobject没有指向:“com/android/newlineactivity/NewLineGLSurfaceView”,因为我可能将它缓存在另一个JENV中。因此,对于静态成员,这是有效的:env->CallStaticVoidMethod(gJClass,gJMethodID);但对于一个非静态的方法,它不是:env->CallVoidMethod(gJObjectCached,gJMethodID);有意义吗?
gJObjectCached
是全局引用,还是您只是存储了传递到
cachejavaobject
的本地引用?它是全局引用,在我调用env->CallVoidMethod(gJObjectCached,gJMethodID)时有效;但回调()在内部调用requestRender()时崩溃。我甚至尝试了jobject jobj=env->NewObject(gJClass,gJMethodID);并通过它,希望有了正确的env、gjClass和gJMethodID,我可以调用这个方法,它就会工作。。。方法被调用,但在requestRender()处再次崩溃;六羟甲基三聚氰胺六甲醚。。。你的问题有点让人困惑。。。jobject是缓存的;在我的.cpp文件中声明为全局,我在其中存储传递到cachejavaobject的本地引用,使用:JNIEXPORT void Java_com_android_OpenGL_NativeLib_cachejavaobject(JNIEnv env,jobject obj){//缓存Java对象gJObjectCached=env->NewGlobalRef(obj);lognativeToAndroText(“gObjectCached global”)}如果您正在执行
gJObjectCached=env->NewGlobalRef(obj)
,那么您正在存储一个全局引用,这是您应该执行的操作。如果这样做之后事情仍然不起作用,恐怕我没有建议了。
JNIEXPORT void Java_com_android_OpenGL_NativeLib_cachejavaobject(JNIEnv* env, jobject obj)
{
    // cache the java object
    gJObjectCached = obj;
...
}
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!!!");
}