У меня есть нативное приложение для Android, использующее оболочку Google native_app_glue.Я хотел бы получить менее чем полноэкранную поверхность для рендеринга GLES в.В приложениях GLES, использующих уровень Java, полученный из Activity, это достигается с помощью getWindow (). SetLayer () на уровне Java.Однако ситуация в моем проекте не позволяет мне использовать это решение.
С уровнем nativeActivtiy и native_app_glue я могу использовать JNI для получения классов Java и обратного вызова в Java, но не могу изменять иерархию View.При обратном вызове setLayers () из моего C-кода через JNI я получаю эту ошибку, поскольку 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) ditch nativeприложение склеивает оболочку и запускает собственный код (или, по крайней мере, его часть) в том же потоке, что и JVM; б) наследует от NativeActivity класс Java, который создает иерархию View через setContentView () c) в собственном коде, который выполняется в том же потоке, что иJava использует JNI для вызова setLayout () d) остальная часть нативного кода может выполняться в другом потоке по мере необходимости
Я не уверен, выполним ли описанный выше подход, если я столкнусь с препятствием с этим.