Android 4.1虚拟键盘卡住

标签 android android-ndk java-native-interface

我有以下仅在 Android 4.1 上出现的奇怪问题:当虚拟键盘打开并且用户按下“返回”按钮时,我的应用程序只是卡住并且不再有反应。过了一会儿,弹出一个系统对话框,通知我该应用程序已死亡并将被关闭。我很难想象这是 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

最佳答案

谢谢你提出这个问题,它对我有帮助。

我也看到了卡住。它似乎来自 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。

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

关于Android 4.1虚拟键盘卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14607036/

相关文章:

大多数时候不会显示 Android AdMob 广告

java - 如何调查随机的 Android native 函数调用错误?

Java - 通过相对路径加载 dll 并将它们隐藏在 jar 中

android - Assets 文件夹中的 Sqlite 数据库

android - 多个项目react-native android构建错误

c++ - 为什么 eglMakeCurrent 因 EGL_BAD_ALLOC 而失败?

android - 错误 : Type 'JNIENV' could not be resolved

Logcat 中的 Android-ndk 输出无法识别

c++ - 调用java方法

android - 如何将 libvpx 构建为静态库