2013-04-09 12 views
5

Rozwijamy niezależną grę dla Androida i chcemy, aby użytkownik wybrał swój pseudonim. Zdecydowaliśmy się użyć Natywnej Aktywności dostarczonej przez NDK, ponieważ wydawało się, że jest to najłatwiejsza droga.Awaria podczas zamykania miękkiej klawiatury podczas korzystania z natywnej aktywności

Pierwszym problemem się spotkałem z klawiaturą było to, że ANativeActivity_showSoftInput function() wydaje się zrobić nic w ogóle (jak opisano np here), więc wywołać klawiaturę za pomocą JNI wzywa do działania:

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

Działa to dobrze na klawiaturze i działa dobrze na niektórych urządzeniach razem. Ale na innych urządzeniach (np Nexus 7), gdy użytkownik próbuje zamknąć klawiaturę naciskając „ukryć klawiaturę” przycisk aplikacja zawiesza z tego wyjścia debugowania:

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 

Następnie użytkownik jest przedstawiona z okna pudełko z napisem:

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

Czy jest coś, co robimy oczywiście źle? A może to błąd? Wydaje się, że takie problemy jak this one sugerują, że funkcjonalność klawiatury nigdy nie została właściwie zaimplementowana w natywnym kleju.

Na marginesie, nie testowaliśmy jeszcze na wielu urządzeniach, ale te, na których się nie zawiesił, to te ze starszym systemem operacyjnym Android. Ponadto w przypadku wystąpienia awarii, po pojawieniu się klawiatury, zmienia ona przycisk z tego, który wygląda tak, jak ten backward arrow shaped button na taki, który wygląda tak, jak ten V shaped button. Być może odpowiada to innemu incydentowi wejściowemu, którego nie uwzględniono przy opracowywaniu pierwotnego kleju? Tylko zgaduję .

W każdym razie, jeśli ktoś ma klawiaturę działającą w trybie natywnym, poinformuj nas, jak to zrobiłeś.

Cheers

UPDATE

Donoszono jako błąd w Androidzie here, chcielibyśmy nadal być szczęśliwy, aby usłyszeć o obejściach chociaż. Jeśli jesteś nim dotknięty, możesz oddać głos w tej sprawie (naciskając gwiazdkę).

Odpowiedz

3

Rozwiązanie Petera działa dobrze. Jeśli jednak nie chcesz zmodyfikować plik native_app_glue: zauważ, że process_input jest przypisany jako wskaźnik funkcji. W pliku wdrażania, tworzyć własne funkcje process_input opisany przez Piotra:

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

Na początku android_main swojej funkcji, należy przypisać wersję process_input do android_app-> inputPollSource.process.

W programie obsługi zdarzeń upewnij się, że sprawdziłeś klucz powrotu (AKEYCODE_BACK) i przechwyciłeś go, aby ukryć klawiaturę, jeśli jest widoczna.

Zauważ, że ten problem wydaje się istnieć w systemach Android 4.1 i 4.2 - rozwiązany w 4.3

+0

I całkowicie brakowało, że o wiele lepiej niż łatanie C-plik, dziękuję. –

+0

Przez przypadek dostałam uaktualnienie dla mojego Nexus dziś i wydaje się, że problem został rozwiązany w systemie Android 4.3. Twoja sugestia ułatwia stosowanie poprawionej funkcji process_input tylko wtedy, gdy wersja Androida ma 4.2. Jeśli chcesz, możesz połączyć i moje odpowiedzi, aby zapewnić jedno ostateczne rozwiązanie dla innych. Oznaczę to jako ostateczną odpowiedź. W przeciwnym razie, jeśli sobie przypomnę, spróbuję to zrobić w przyszłym tygodniu. Twoje zdrowie. –

+0

Jasne - aktualizowana. Zauważyłem również, że problem został rozwiązany w 4.3! Szkoda, że ​​będzie się utrzymywał na rynku na telefonach 4.1 i 4.2 przez chwilę ... – krsteeve

2

OK, jak wspomniano w AKTUALIZACJI mojego pierwotnego pytania, jest to błąd występujący gdzieś w systemie operacyjnym Android. Znaleźliśmy obejście, jest naprawdę brzydkie, ale działa, więc ktoś może uznać to za przydatne.

Najpierw trzeba zmodyfikować plik

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

zmieniając funkcję process_input wyglądać tak:

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

Następnie trzeba mieć kod, który wygląda mniej więcej tak wewnątrz własnego programu obsługi zdarzeń wejściowych:

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

Aby uzyskać numery wersji systemu operacyjnego używamy innych wywołań JNI, aby uzyskać je od android.os.Build.VERSION.RELEASEandroid.os.Build.VERSION.SDK_INT.Aby wdrożyć show_keyboard i hide_keyboard wykorzystanie informacji z Ratamovics odpowiedzi w this postu.

UWAGA Aby mieć android_native_app_glue.c zestawiane automatycznie i uniknąć robi zmian bezpośrednio do drzewa NDK, można skopiować plik do katalogu/JNI swojego projektu i rowu te dwie linie z Android. mk

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

Mam ten problem na Nexusie 4 z Androidem 4.2. Dziękuję [DUŻO] za udokumentowanie tego problemu! – Gaetan

Powiązane problemy