Google の native_app_glue ラッパーを使用したネイティブ Android アプリがあります。GLES をレンダリングするためのフルスクリーン未満のサーフェスを取得したいと考えています。Activity から派生した Java レイヤーを使用する GLES アプリでは、これは Java レイヤーの getWindow().setLayer() によって実現されます。ただし、私のプロジェクトの状況では、このソリューションを使用できません。
nativeActivtiy と native_app_glue レイヤーを使用すると、JNI を使用して Java クラスを取得し、Java にコールバックできますが、View 階層を変更することはできません。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) setContentView() を介して View 階層を作成する Java クラスを NativeActivity から派生させる c) Java と同じスレッドで実行されるネイティブ コードで JNI を使用して setLayout() を呼び出す d) ネイティブ コードの残りの部分は、必要に応じて別のスレッドで実行することができる
上記のアプローチが実行可能かどうか、これで障害に遭遇するかどうかはわかりません。