Crash when closing soft keyboard while using native activity

后端 未结 2 1821
無奈伤痛
無奈伤痛 2021-02-10 01:45

We are developing an indie game for android and would like the user to choose his nickname. We have chosen to use the Native Activity that is provided by the NDK as that seemed

相关标签:
2条回答
  • 2021-02-10 02:29

    Peter's solution works well. However if you don't want to modify the native_app_glue file: notice that process_input is assigned as a function pointer. In your implementation file, create your own process_input functions as described by Peter:

    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));
        }
    }
    

    At the beginning of your android_main function, assign your version of process_input to android_app->inputPollSource.process.

    In your event handler make sure you check for the back key (AKEYCODE_BACK) and intercept it to hide your keyboard if visible.

    Note that this problem appears to exist in Android 4.1 and 4.2 - solved in 4.3

    0 讨论(0)
  • 2021-02-10 02:38

    OK, as mentioned in the UPDATE of my original question, this is a bug somewhere inside the Android OS. We've found a workaround, it is really ugly but it works so someone might find it useful.

    First you'll need to modify the file

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

    by changing the function process_input to look like this:

    // 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));
        }
    }
    

    Then you should have a code that looks something like this inside your own input event handler:

    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;  
    }
    

    To get the OS version numbers we use another JNI calls to get it from android.os.Build.VERSION.RELEASE android.os.Build.VERSION.SDK_INT. To implement the show_keyboard and hide_keyboard use information from Ratamovics answer in this post.

    NOTE To have the android_native_app_glue.c compiled automatically and to avoid doing changes directly to the NDK tree, you might want to copy the file to the jni/ directory of your project and ditch these two lines from your Android.mk

    LOCAL_STATIC_LIBRARIES := android_native_app_glue
    $(call import-module,android/native_app_glue)
    
    0 讨论(0)
提交回复
热议问题