안드로이드 앱의 충돌 방지에 한 일

요 전날 Google Play에 Android 앱을 출시했지만 일주일에 약 20 개의 충돌 보고서가 올랐습니다.
보고서는 20 건 정도 있었지만, 대략 2 건의 대책을 했으므로 메모 해 둡니다

해결의 보증은 할 수 없기 때문에 나쁘지 않고

했던 일



TextInputEditText 구현 수정


  • noExcludeDescendants 추가
  • hint 속성은 TextinputLayout에 붙인다
  • setOnEditorActionListener 삭제

  • MapView null 체크 추가



    MapActivity.kt
    override fun onDestroy() {
            try { 
                binder?.mapView?.onDestroy()
            } catch (e : NullPointerException) {
                // Send report exception..
            }
            super.onDestroy()
        }
    

    충돌 보고서



    GooglePlayConsole의 사이드바 '장애 및 ANR'에서 충돌 보고서를 확인할 수 있습니다.
    이런 식으로 상당한 수가보고되었습니다.



    많이 나와서 눈이 눈에 띄는 것 같습니다만, 대처할 수 있을 것 같은 것은 대략 2종류에 들어갔습니다

    TextInputEditText 관련



    setHint 주위의 예외


    java.lang.NullPointerException: 
      at android.widget.Editor.stopTextActionMode (Editor.java:2687)
      at android.widget.Editor.prepareCursorControllers (Editor.java:822)
      at android.widget.TextView.nullLayouts (TextView.java:9153)
      at android.widget.TextView.checkForRelayout (TextView.java:10101)
      at android.widget.TextView.setHintInternal (TextView.java:6534)
      at android.widget.TextView.setHint (TextView.java:6523)
      at com.google.android.material.textfield.TextInputLayout.dispatchProvideAutofillStructure (TextInputLayout.java:1280)
      at android.view.ViewGroup.dispatchProvideAutofillStructure (ViewGroup.java:3774)
      at android.view.ViewGroup.dispatchProvideAutofillStructure (ViewGroup.java:3774)
      at android.app.assist.AssistStructure$WindowNode.<init> (AssistStructure.java:517)
      at android.app.assist.AssistStructure.<init> (AssistStructure.java:2047)
      at android.app.ActivityThread.handleRequestAssistContextExtras (ActivityThread.java:3386)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1899)
      at android.os.Handler.dispatchMessage (Handler.java:106)
      at android.os.Looper.loop (Looper.java:214)
      at android.app.ActivityThread.main (ActivityThread.java:7050)
      at java.lang.reflect.Method.invoke (Native Method)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
    

    삼성 기기별 양식 길게 누르기 문제 라고 생각했지만, 미묘하게 다르게 보입니다.
    (performLongClick이 얽혀 있지 않음)
    그럼에도 불구하고 이번 충돌은 삼성 단말기였습니다.

    AssistStructure$WindowNode 예외


    java.lang.NullPointerException: 
      at android.app.assist.AssistStructure$WindowNode.<init> (AssistStructure.java:484)
      at android.app.assist.AssistStructure.<init> (AssistStructure.java:1908)
      at android.app.ActivityThread.handleRequestAssistContextExtras (ActivityThread.java:3040)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1812)
      at android.os.Handler.dispatchMessage (Handler.java:105)
      at android.os.Looper.loop (Looper.java:251)
      at android.app.ActivityThread.main (ActivityThread.java:6563)
      at java.lang.reflect.Method.invoke (Native Method)
      at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:240)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:767)
    

    여기는 Android8 시스템 문제 와 같고, 실제로 Android8의 3 디바이스에서 발생하고 있었습니다
    링크처의 기사를 쫓으면 알 수 있습니다만, 이쪽도 TextInputEditText 관련입니다

    처치



    이 블로그 기사 이나 상기 StackOverFlow에서도 언급되고 있습니다만,
  • 양식 자동 입력 Autofill 비활성화 (noExcludeDescendants)
  • hint를 삽입 할 위치를 TextInputEditText -> TextInputLayout으로 바꿉니다.

    를 실시했습니다. 입력 양식 주위는 조금 불안정합니까?

    게다가 별로 상관 없을지도 모르지만 setOnEditorActionListener를 무효화했습니다.
    사용자 정보 입력 양식이 이번 문제였습니다만, 키보드의 엔터 버튼을 사용할 수 없어도 UX에 큰 영향은 없다고 판단했습니다

    MapView 관련


    java.lang.NullPointerException: 
      at com.google.maps.api.android.lib6.impl.cy.f (com.google.android.gms.dynamite_mapsdynamite@[email protected] (120400-0):1)
      at com.google.android.gms.maps.internal.q.aX (com.google.android.gms.dynamite_mapsdynamite@[email protected] (120400-0):12)
      at dt.onTransact (com.google.android.gms.dynamite_mapsdynamite@[email protected] (120400-0):4)
      at android.os.Binder.transact (Binder.java:914)
      at com.google.android.gms.internal.maps.zza.zzb (Unknown Source:20)
      at com.google.android.gms.maps.internal.zzk.onDestroy (Unknown Source:26)
      at com.google.android.gms.maps.MapView$zza.onDestroy (Unknown Source:34)
      at com.google.android.gms.dynamic.DeferredLifecycleHelper.onDestroy (Unknown Source:71)
      at com.google.android.gms.maps.MapView.onDestroy (Unknown Source:40)
      at com.example.android.fragment.ExmapleFragment.onLowMemory (LastTripFragment.kt:231)
      at androidx.fragment.app.Fragment.performLowMemory (Fragment.java:3069)
      at androidx.fragment.app.FragmentManager.dispatchLowMemory (FragmentManager.java:3152)
      at androidx.fragment.app.Fragment.performLowMemory (Fragment.java:3070)
      at androidx.fragment.app.FragmentManager.dispatchLowMemory (FragmentManager.java:3152)
      at androidx.fragment.app.FragmentController.dispatchLowMemory (FragmentController.java:379)
      at androidx.fragment.app.FragmentActivity.onLowMemory (FragmentActivity.java:332)
      at android.app.ActivityThread.handleLowMemory (ActivityThread.java:6002)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1921)
      at android.os.Handler.dispatchMessage (Handler.java:107)
      at android.os.Looper.loop (Looper.java:359)
      at android.app.ActivityThread.main (ActivityThread.java:7418)
      at java.lang.reflect.Method.invoke (Native Method)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:935)
    

    GoogleMap을 표시하는 MapView와 관련 Fragment의 라이프 사이클 시스템에서 다발하고 있습니다.
    이 예에서는 onLowMemory이지만 onStart, onPause, onDestory 등에서도 비슷한 예외가 나왔습니다.

    MapView 공식 문서 에서 언급했듯이 라이프 사이클과 MapView를 연결했지만 그 때 Map 객체가 존재하지 않는 것 같습니다.

    API를 완전한 대화형 모드로 사용하는 경우 MapView 클래스에서 활동 수명 주기 메서드(onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy(), onSaveInstanceState() , onLowMemory())를 MapView 클래스의 해당 메서드로 전송해야 합니다.

    ExampleFragment.kt
    override fun onDestroy() {
        binder.mapView.onDestroy()  // ←ここでmapViewがnullになってる
        super.onDestroy()
    }
    

    처치



    이쪽에 대해서는 명확한 해결책을 찾을 수 없고, 우선 예외를 잡기로 했습니다↓

    ExampleFragment.kt
    override fun onDestroy() {
            try {
                binder.mapView.onDestroy()
            } catch(e: Exception) {
                /* do nothing */
            }
            super.onDestroy()
        }
    

    이제 먼저 모습을 살펴 보겠습니다.

    tgkill


    backtrace:
      #00  pc 000000000006b970  /system/lib64/libc.so (tgkill+8)
      #00  pc 0000000000068df4  /system/lib64/libc.so (pthread_kill+64)
      #00  pc 0000000000024290  /system/lib64/libc.so (raise+24)
      #00  pc 000000000001ccac  /system/lib64/libc.so (abort+52)
      #00  pc 000000000042cca8  /system/lib64/libart.so (art::Runtime::Abort()+352)
      #00  pc 00000000000e4c24  /system/lib64/libart.so (art::LogMessage::~LogMessage()+1204)
      #00  pc 000000000024ab24  /system/lib64/libart.so (art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)+308)
      #00  pc 0000000000324684  /system/lib64/libart.so (art::JNI::FindClass(_JNIEnv*, char const*)+2824)
    

    크래시 리포트 중(안)에서도 한층 이채를 발하고 있는 것이 이쪽으로, linux의 시스템 콜일까? 생각합니다.
    이것에 관해서는 스택 트레이스가 없기 때문에 원인 식별이 어려워, 또 꽤 한정된 디바이스로 밖에 나와 있지 않기 때문에 대응을 보답하고 있습니다
    (Android7계의 낡은 디바이스로 나오는 것 같습니다)

    요약



    그래서 안드로이드 충돌 보고서에 대해 나름대로 대책을 넣어 보았습니다.
    입력 양식과지도 당이 다소 불안정한 느낌입니다.
    일단 이것으로 모습을 보자고 생각합니다.
  • 좋은 웹페이지 즐겨찾기