1

我有以下仅在 Android 4.1 上出现的奇怪问题:当打开虚拟键盘并且用户按下 BACK 按钮时,我的应用程序只是冻结并且不再反应。过了一会儿会弹出一个系统对话框,通知我该应用程序已死并且将被关闭。我很难想象这是 Android 中的一个错误,因为通过按 BACK 按钮隐藏键盘是应该正常工作的基本功能。不过,我的代码太小了,几乎可以完全排除代码中的错误。

这是我的代码供您查看:

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 是我正在使用的全局,如果软键盘正在显示,这是真的。

编辑:然后我关闭了在输入处理程序中处理后按的软键盘。

于 2013-05-05T06:34:07.527 回答