2013-04-09 95 views
5

我们正在开发针对android的独立游戏,并希望用户选择他的昵称。我们选择使用NDK提供的本地活动,因为这似乎是最简单的方法。在使用本地活动时关闭软键盘时发生崩溃

我们用键盘遇到的第一个问题是,该功能ANativeActivity_showSoftInput()似乎都做什么(例如如here),所以我们使用JNI调用函数弹出键盘:

static void showKeyboard(Activity activity) { 
    String s = Context.INPUT_METHOD_SERVICE; 
    InputMethodManager m = (InputMethodManager)activity.getSystemService(s); 
    View w = activity.getWindow().getDecorView(); 
    m.showSoftInput(w, 0); 
} 

这适用于调出键盘,并在一些设备上一起工作良好。但是,在其他设备(如Nexus 7保护),当用户试图通过点击“隐藏键盘”关闭键盘按钮的应用程序冻结与此调试输出:

I/InputDispatcher( 453): Application is not responding: AppWindowToken{429b54a8 token=Token{42661288 ActivityRecord{41bb0b00 u0 com.example.project/android.app.NativeActivity}}} - Window{420d6138 u0 com.example.project/android.app.NativeActivity}. It has been 5006.7ms since event, 5005.6ms since wait started. Reason: Waiting because the focused window has not finished processing the input events that were previously delivered to it. 
I/WindowManager( 453): Input event dispatching timed out sending to com.example.project/android.app.NativeActivity 

,然后用户会看到一个对话框盒说:

Project isn't responding. Do you want to close it? [Wait]/[OK] 

有什么我们正在做的显然是错的?或者这可能是一个错误?像this one这样的问题似乎表明键盘功能从未在本地胶水中正确实施。

在附注中,我们还没有在很多设备上进行过测试,但没有崩溃的是使用较旧的android操作系统的测试。另外,在出现崩溃的那些键盘出现时,它会将背面的按钮从看起来像backward arrow shaped button的按钮更改为看起来像这样的一个按钮 V shaped button。也许这对应于不同的输入事件,当他们第一次开发本地粘合剂时没有考虑到这一点?我只是猜测。

无论如何,如果有人在使用本地活动时使用软键盘工作,请告诉我们您是如何做到的。

干杯

UPDATE

据报道,在Android的here的错误,我们仍然很乐意听到的解决方法,但。如果你也受到它的影响,你可能想在这个问题上投票(按星号)。

回答

3

Peter的解决方案效果很好。但是,如果您不想修改native_app_glue文件:请注意process_input被分配为函数指针。在实现文件中,由彼得描述创建自己的process_input功能:

static void process_input(struct android_app* app, struct android_poll_source* source) { 
    AInputEvent* event = NULL; 
    if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { 
     int type = AInputEvent_getType(event); 
     LOGV("New input event: type=%d\n", AInputEvent_getType(event)); 

     bool skip_predispatch 
       = AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY 
       && AKeyEvent_getKeyCode(event) == AKEYCODE_BACK; 

     // skip predispatch (all it does is send to the IME) 
     if (!skip_predispatch && AInputQueue_preDispatchEvent(app->inputQueue, event)) { 
      return; 
     } 

     int32_t handled = 0; 
     if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); 
     AInputQueue_finishEvent(app->inputQueue, event, handled); 
    } else { 
     LOGE("Failure reading next input event: %s\n", strerror(errno)); 
    } 
} 

在你android_main功能的开始,分配您process_inputandroid_app-> inputPollSource.process的版本。

在您的事件处理程序中,确保您检查后退键(AKEYCODE_BACK)并拦截它以隐藏键盘(如果可见)。

请注意此问题出现在Android的4.1和4.2存在 - 4.3

+0

我完全错过了,比修补c文件好多了,谢谢。 – 2013-07-30 21:15:51

+0

偶然地,我今天收到了Nexus的升级版本,看起来问题已在Android 4.3中得到修复。只有在Android版本为4.2时,您的建议才能轻松应用修补后的process_input功能。如果你愿意,你可以结合你和我的答案为其他人提供最终的解决方案。然后我会将其标记为最终答案。否则,如果我记得,我会在下周的某个时候尝试。干杯。 – 2013-07-30 22:57:49

+0

当然 - 更新。我也注意到这个问题已经在4.3中解决了!太糟糕了,它会在4.1和4.2手机市场上持续一段时间... – krsteeve 2013-07-31 01:10:33

2

好的,正如我原来的问题的更新中提到的那样,这是Android操作系统内部某处的错误。我们找到了一种解决方法,它非常难看,但它起作用,所以有人会觉得它很有用。

首先,您需要通过改变功能process_input修改文件

<NDK>/sources/android/native_app_glue/android_native_app_glue.c 

看起来像这样:

// When user closes the software keyboard, this function is normally not 
// called at all. On the buggy devices, it is called as if AKEYCODE_BACK 
// was pressed. This event then gets consumed by the 
// AInputQueue_preDispatchEvent. There should be some mechanism that then 
// calls the process_input again to finish processing the input. 
// But it never does and AInputQueue_finishEvent is never called, the OS 
// notices this and closes our app. 
static void process_input(struct android_app* app 
         , struct android_poll_source* source) { 
    AInputEvent* event = NULL; 
    if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { 
     int type = AInputEvent_getType(event); 
     LOGV("New input event: type=%d\n", AInputEvent_getType(event)); 

     int skip_predispatch 
       = AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY 
       && AKeyEvent_getKeyCode(event) == AKEYCODE_BACK; 

     // TODO: Not sure if we should skip the predispatch all together 
     //  or run it but not return afterwards. The main thing 
     //  is that the code below this 'if' block will be called. 
     if (!skip_predispatch) { 
      if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { 
       return; 
      } 
     } 

     int32_t handled = 0; 
     if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); 
     AInputQueue_finishEvent(app->inputQueue, event, handled); 
    } else { 
     LOGE("Failure reading next input event: %s\n", strerror(errno)); 
    } 
} 

那么你应该有一个看起来像这样的代码在您自己的输入事件处理程序中:

static int32_t handle_input(android_app* app, AInputEvent* event) { 
    int32_t handled = 0; 

    struct engine* engine = (struct engine*) app->userData; 
    switch (AInputEvent_getType(event)) { 
    case AINPUT_EVENT_TYPE_KEY: 
     switch (AKeyEvent_getAction(event)) { 
     case AKEY_EVENT_ACTION_DOWN: 
      int key = AKeyEvent_getKeyCode(event); 
      if (os_version_major == 4 && os_version_minor == 2) { 
      if (m_keyboard_is_visible && key == AKEYCODE_BACK) { 
       // You should set this to true when showing the keyboard. 
       m_keyboard_is_visible = false; 
       hide_keyboard(); 
       handled = 1; 
       break; 
      } 
      } 
      ... // your own "key down" event handling code. 
      break; 
     } 
     break; 
    ... 
    } 
    return handled; 
} 

要获取操作系统版本号,我们使用另一个JNI调用来获取它android.os.Build.VERSION.RELEASEandroid.os.Build.VERSION.SDK_INT。实施show_keyboardhide_keyboard使用来自Ratamovics的信息回答于this后。

注意要使android_native_app_glue.c自动编译和避免直接做更改NDK树,你可能需要将文件复制到你的项目的JNI /目录和沟从Android这两条线。 mk

LOCAL_STATIC_LIBRARIES := android_native_app_glue 
$(call import-module,android/native_app_glue) 
+0

解决了我在Nexus 4这个问题与Android 4.2。谢谢[很多]记录这个问题! – Gaetan 2013-07-30 16:20:07