1

我有使用 Google 的 native_app_glue 包装器的原生 Android 应用程序。我想获得一个小于全屏的表面来渲染 GLES。在使用从 Activity 派生的 java 层的 GLES 应用程序中,这是通过 Java 层中的 getWindow().setLayer() 完成的。但是,我的项目情况不允许我使用此解决方案。

通过 nativeActivtiy 和 native_app_glue 层,我可以使用 JNI 获取 Java 类和回调到 Java,但不能修改视图层次结构。通过 JNI 从我的 C 代码回调 setLayers() 时,我收到此错误,因为 NativeActivity 与创建 View 层次结构的线程不在同一个线程中。

E/AndroidRuntime(21503): android.view.ViewRoot$CalledFromWrongThreadException: 只有创建视图层次结构的原始线程才能触摸它的视图。

这是我的代码:

// Call Java to set  Window size
//-----------------------------------------------------------------------------
int CallJavaWindowSize(struct android_app* state, jint width, jint height)
//-----------------------------------------------------------------------------
{
    JNIEnv *env;
    jclass nativeActivityClass;
    jobject nativeActivityObj;
    jmethodID mid;
    jobject windowObj;
    bool didAttachment = false;
    int ret = -1;
    JavaVMAttachArgs JVMAttachArgs;

    jint result = state->activity->vm->GetEnv((void**) &env, JNI_VERSION_1_6); 

    if (!env  && result == JNI_EDETACHED)
    {
        JVMAttachArgs.version = JNI_VERSION_1_6;
        JVMAttachArgs.name = "NativeThread";
        JVMAttachArgs.group = NULL;
        if (state->activity->vm->AttachCurrentThread(&env, NULL) < 0)
        {
            __android_log_print(ANDROID_LOG_ERROR, "PowerLift", "CallJavaWindowSize() Failed to attach to thread");
            return ret;
        }
        __android_log_print(ANDROID_LOG_DEBUG, "PowerLift", "CallJavaWindowSize() attached to Thread");
        didAttachment = true;
    }
    else if (result < 0)
    {
            __android_log_print(ANDROID_LOG_ERROR, "PowerLift", "CallJavaWindowSize() Failed to GetEnv()");
            return ret;
    }

    // retrieves NativeActivity class
    nativeActivityObj = state->activity->clazz;
    //nativeActivityClass = env->FindClass("android/app/NativeActivity");
    nativeActivityClass = env->GetObjectClass(nativeActivityObj);
    if (!nativeActivityClass)
    {
        __android_log_print(ANDROID_LOG_ERROR, "PowerLift", "CallJavaWindowSize() Failed to Find NativeActivity class");
        return ret;
    }

    //Run getWindow().setLayout(width,height)
    mid = env->GetMethodID(nativeActivityClass, "getWindow", "()Landroid/view/Window;");
    if (mid == 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "PowerLift", "CallJavaWindowSize() Failed to get method getWindow() with signature = ()Landroid/view/Window;");
        return ret;
    }

    windowObj = env->CallObjectMethod(nativeActivityObj, mid);
    if (windowObj == 0)
    {
        __android_log_print(ANDROID_LOG_ERROR, "PowerLift", "CallJavaWindowSize() Failed to CallObjectMethod for mid getWindow()");
        return ret;
    }

    jclass classWindow = env->FindClass("android/view/Window");
    mid = env->GetMethodID(classWindow, "setLayout", "(II)V");
    env->CallVoidMethod(windowObj, mid, width, height);


    if (didAttachment)
        state->activity->vm->DetachCurrentThread();

    return 0;
}

你们中的一些人可能建议的解决方案是使用 glViewport() 绘制到小于全屏。此解决方案在视觉上有效,但性能很差,因为 EGL 驱动程序仍在处理全屏表面。

我想知道这种方法是否是最好的解决方案,因为它在架构上与使用本机应用程序包装器相比有很大的变化:a)放弃本机应用程序胶水包装器并在与 JVM 相同的线程中运行本机代码(或至少一部分)b)从 NativeActivity 派生一个 Java 类,它通过 setContentView() 创建视图层次结构 c) 在与 Java 相同的线程中运行的本机代码中使用 JNI 调用 setLayout() d) 其余的本机代码可以根据需要在不同的线程中运行

我不确定上述方法是否可行,我是否会遇到障碍。

4

1 回答 1

0

如果您想从像素缓冲区中渲染出来,您可能需要使用glSubTexImage2D().

于 2013-03-29T16:46:57.547 回答