1

Android 4.1 でのみ発生する次の奇妙な問題があります。仮想キーボードを開いてユーザーが [戻る] ボタンを押すと、アプリがフリーズして反応しなくなります。しばらくすると、システム ダイアログ ボックスがポップアップ表示され、アプリが終了して閉じられることが通知されます。BACK ボタンを押してキーボードを非表示にすることは基本的な機能であるため、これが Android のバグであるとはほとんど想像できません。それでも、私のコードは非常に小さいので、コード内のエラーをほぼ完全に排除できます。

レビューするコードは次のとおりです。

static int quit = 0;

static void engine_handle_cmd(struct android_app *app, int32_t cmd)
{       
        switch(cmd) {
        case APP_CMD_TERM_WINDOW:
                quit = 1;
                break;
        }
}

static int32_t engine_handle_input(struct android_app *app, AInputEvent *event)
{       
        switch(AInputEvent_getType(event)) {
        case AINPUT_EVENT_TYPE_MOTION: {
                int action = AMotionEvent_getAction(event);
                if((action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) showkeyboard(app);                
                break;
                }                               
        }

        return 0;
}

void android_main(struct android_app* state)
{       
        int events, fd;
        struct android_poll_source *source;

        app_dummy();

        state->onAppCmd = engine_handle_cmd;
        state->onInputEvent = engine_handle_input;

        while(!quit) {
                if(ALooper_pollOnce(-1, &fd, &events, (void **) &source) >= 0) {
                        if(source) source->process(state, source);                                      
                }
        }       

        exit(0);
}

showkeyboard() 関数は、次のように JNI で実装されます。

static void showkeyboard(struct android_app* state)
{
        // Attaches the current thread to the JVM.
        jint lResult;
        jint lFlags = 0;
        JavaVM *lJavaVM = state->activity->vm;
        JNIEnv *lJNIEnv = state->activity->env;
        JavaVMAttachArgs lJavaVMAttachArgs;
        jobject lNativeActivity, INPUT_METHOD_SERVICE;
        jclass ClassNativeActivity, ClassContext;
        jfieldID FieldINPUT_METHOD_SERVICE;
        JNIEnv *env;
        int attached = 0;

        // must check if we're already attached!
        switch((*lJavaVM)->GetEnv(lJavaVM, (void**) &env, JNI_VERSION_1_6)) {
        case JNI_OK:
                break;
        case JNI_EDETACHED:
                lJavaVMAttachArgs.version = JNI_VERSION_1_6;
                lJavaVMAttachArgs.name = "NativeThread";
                lJavaVMAttachArgs.group = NULL;

                lResult = (*lJavaVM)->AttachCurrentThread(lJavaVM, &lJNIEnv, &lJavaVMAttachArgs);
                if(lResult == JNI_ERR) return;          

                attached = 1;
                break;

        case JNI_EVERSION:
                return;  // Invalid Java version
        }

        // Retrieves NativeActivity.
        lNativeActivity = state->activity->clazz;
        ClassNativeActivity = (*lJNIEnv)->GetObjectClass(lJNIEnv, lNativeActivity);

        // Retrieves Context.INPUT_METHOD_SERVICE.
        ClassContext = (*lJNIEnv)->FindClass(lJNIEnv, "android/content/Context");

        FieldINPUT_METHOD_SERVICE = (*lJNIEnv)->GetStaticFieldID(lJNIEnv, ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
        INPUT_METHOD_SERVICE = (*lJNIEnv)->GetStaticObjectField(lJNIEnv, ClassContext, FieldINPUT_METHOD_SERVICE);
{
        // Runs getSystemService(Context.INPUT_METHOD_SERVICE).
        jclass ClassInputMethodManager = (*lJNIEnv)->FindClass(lJNIEnv, "android/view/inputmethod/InputMethodManager");
        jmethodID MethodGetSystemService = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
        jobject lInputMethodManager = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);

        // Runs getWindow().getDecorView().
        jmethodID MethodGetWindow = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
        jobject lWindow = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lNativeActivity, MethodGetWindow);
        jclass ClassWindow = (*lJNIEnv)->FindClass(lJNIEnv, "android/view/Window");
        jmethodID MethodGetDecorView = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassWindow, "getDecorView", "()Landroid/view/View;");
        jobject lDecorView = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lWindow, MethodGetDecorView);

        // Runs lInputMethodManager.showSoftInput(...).
        jmethodID MethodShowSoftInput = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
        (*lJNIEnv)->CallBooleanMethod(lJNIEnv, lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags);

        if(attached) {
                // Finished with the JVM.
                (*lJavaVM)->DetachCurrentThread(lJavaVM);
        }
}
}

再現するには、アプリをコンパイルし、Android 4.1 イメージを使用してエミュレーターに配置し、どこかをクリックすると、仮想キーボードが表示されます。ここで、BACK ボタンを使用してキーボードを閉じてみてください。アプリはすぐにフリーズします。

このエラーは Android 4.1 でのみ発生することに注意してください。2.3、3.0、4.0 で問題なく動作します。これは Android 自体の重大なバグでしょうか? 私のコードは実際には何もしない原始的なメイン ループにすぎないため、他に説明はありません。ただし、仮想キーボードを閉じようとするとアプリがクラッシュします。

この問題について助けてくれてありがとう!私はこれに何時間も取り組んできました:(

更新 1: この問題は Android 4.2 でも発生します。Android の問題サイトでチケットが開かれました。Android 開発者がそれを認識できるように、同じ問題に遭遇したすべての人がチケットにコメントする必要があります。リンクは次のとおりです。 http://code.google.com/p/android/issues/detail?id=43817&thanks=43817&ts=1359632204

4

1 に答える 1

1

これを聞いてくれてありがとう、それは私を助けました。

フリーズも見られました。android_native_app_glue.c の process_input の AInputQueue_preDispatchEvent から来ているようです。それを回避するために、次のように変更しました。

LOGI("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputQueue_preDispathEvent(app->inputQueue, event)) {
    return;
}
int32_t handled = 0;

に:

int32_t type = AInputEvent_getType(event);
LOGI("New input event: type=%d\n", type);
if (!g_iShowingSoftKeyboard || (type != AINPUT_EVENT_TYPE_KEY)
    || (AKeyEvent_getKeyCode(event) != AKEYCODE_BACK))
{
    if (AInputQueue_preDispathEvent(app->inputQueue, event)) {
        return;
    }
}
int32_t handled = 0;

もちろん、g_iShowingSoftKeyboard は私が使用しているグローバルであり、ソフト キーボードが表示されている場合は true です。

編集:そして、入力ハンドラーでバックプレスを処理するソフトキーボードを閉じます。

于 2013-05-05T06:34:07.527 に答える