2013-04-09 43 views
5

Chúng tôi đang phát triển trò chơi độc lập cho android và muốn người dùng chọn biệt hiệu của mình. Chúng tôi đã chọn sử dụng Hoạt động gốc được cung cấp bởi NDK vì đó dường như là cách dễ nhất để đi.Sụp đổ khi đóng bàn phím mềm khi đang sử dụng hoạt động gốc

Vấn đề đầu tiên chúng tôi gặp phải với bàn phím là các chức năng ANativeActivity_showSoftInput() dường như không làm gì cả (như mô tả ví dụ here), vì vậy chúng tôi đưa lên bàn phím sử dụng JNI cuộc gọi đến chức năng:

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

Tính năng này hoạt động tốt khi đưa bàn phím lên và hoạt động tốt trên một số thiết bị. Nhưng trên các thiết bị khác (ví dụ Nexus 7), khi người dùng cố gắng để đóng bàn phím bằng cách nhấn "ẩn bàn phím" nút ứng dụng bị đóng băng với debug đầu ra này:

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 

Và sau đó người dùng sẽ được trình bày với một hộp thoại hộp nói rằng:

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

Có điều gì đó chúng tôi đang làm rõ ràng là sai? Hoặc có thể đây là một lỗi? Các vấn đề như this one dường như đề xuất chức năng bàn phím chưa bao giờ được triển khai đúng cách trong keo gốc.

Trên một lưu ý phụ, chúng tôi chưa thử nghiệm trên nhiều thiết bị, nhưng những nơi mà nó không sụp đổ là những người có hệ điều hành Android cũ hơn. Ngoài ra, đối với những người gặp sự cố, khi bàn phím xuất hiện, nút này sẽ thay đổi nút quay lại từ một cái trông giống như thế này backward arrow shaped button thành nút trông giống như thế này V shaped button. Có lẽ điều đó tương ứng với một sự kiện đầu vào khác không được tính khi lần đầu tiên họ phát triển loại keo gốc? Tôi chỉ đang đoán thôi .

Dù sao, nếu ai đó có bàn phím mềm hoạt động trong khi sử dụng hoạt động gốc, vui lòng cho chúng tôi biết bạn đã làm như thế nào.

Cheers

CẬP NHẬT

Nó đã được báo cáo là một lỗi trong Android here, chúng tôi vẫn sẽ rất vui khi được nghe về cách giải quyết mặc dù. Nếu bạn cũng bị ảnh hưởng bởi nó, bạn có thể muốn bỏ phiếu cho vấn đề đó (bằng cách nhấn dấu sao).

Trả lời

3

Giải pháp của Peter hoạt động tốt. Tuy nhiên nếu bạn không muốn sửa đổi tệp native_app_glue: hãy lưu ý rằng process_input được gán làm con trỏ hàm. Trong tập tin thực hiện của bạn, tạo ra chức năng process_input của riêng bạn như mô tả của 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)); 
    } 
} 

Vào đầu android_main chức năng của bạn, chuyển nhượng các phiên bản của process_input để android_app-> inputPollSource.process.

Trong trình xử lý sự kiện, hãy đảm bảo bạn kiểm tra lại phím (AKEYCODE_BACK) và chặn nó để ẩn bàn phím của bạn nếu hiển thị.

Lưu ý rằng sự cố này dường như tồn tại trong Android 4.1 và 4.2 - được giải quyết trong 4.3

+0

Tôi hoàn toàn bị mất điều đó, tốt hơn nhiều so với vá các tập tin c, cảm ơn bạn. –

+0

Tình cờ, tôi đã nhận được bản nâng cấp cho Nexus của mình hôm nay và có vẻ như sự cố đã được khắc phục trong Android 4.3. Đề xuất của bạn giúp bạn dễ dàng áp dụng hàm process_input được vá chỉ khi phiên bản Android là 4.2. Nếu bạn muốn, bạn có thể kết hợp các câu trả lời của bạn và của tôi để cung cấp một giải pháp cuối cùng cho người khác. Sau đó tôi sẽ đánh dấu nó là câu trả lời cuối cùng. Nếu không, nếu tôi sẽ nhớ, tôi sẽ cố gắng làm điều đó đôi khi vào tuần tới. Chúc mừng. –

+0

Chắc chắn - đã cập nhật. Tôi cũng nhận thấy sự cố được khắc phục ở 4.3! Quá xấu nó sẽ tồn tại trên thị trường trên 4.1 và 4.2 điện thoại trong một thời gian ... – krsteeve

2

OK, như đã đề cập trong CẬP NHẬT câu hỏi ban đầu của tôi, đây là lỗi ở đâu đó bên trong hệ điều hành Android. Chúng tôi đã tìm thấy một cách giải quyết, nó thực sự xấu xí nhưng nó hoạt động để ai đó có thể thấy nó hữu ích.

Trước tiên, bạn sẽ cần phải sửa đổi các tập tin

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

bằng cách thay đổi các chức năng process_input trông như thế này:

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

Sau đó, bạn nên có một mã mà trông giống như thế này bên trong trình xử lý sự kiện nhập của riêng bạn:

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

Để có được số phiên bản hệ điều hành, chúng tôi sử dụng một cuộc gọi JNI khác để nhận số điện thoại từ android.os.Build.VERSION.RELEASEandroid.os.Build.VERSION.SDK_INT.Để triển khai show_keyboardhide_keyboard sử dụng thông tin từ câu trả lời Ratamovics trong this bài đăng.

LƯU Ý Để có android_native_app_glue.c biên soạn tự động và để tránh làm thay đổi ngay trên cây NDK, bạn có thể muốn sao chép các tập tin vào JNI/thư mục của dự án của bạn và mương hai dòng sau từ Android của bạn. mk

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

Tôi gặp sự cố này trên Nexus 4 với Android 4.2. Cảm ơn bạn [A LOT] để ghi lại vấn đề này! – Gaetan

Các vấn đề liên quan