先日GooglePlayにAndroidアプリをリリースしたのですが、1週間でおよそ20件のクラッシュレポートが上がってきました
レポートは20件ほどありましたが、およそ2件の対策をしましたのでメモしておきます
解決の保証はできませんので悪しからず
やったこと
TextInputEditTextの実装修正
- noExcludeDescendantsを追加
- hint属性はTextinputLayoutにつける
- setOnEditorActionListenerを削除
MapViewのnullチェックを追加
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)
Samsung端末固有のフォーム長押し問題かと思いましたが、微妙に違うように見えます
(performLongClickとかが絡んでいない)
とはいえ今回クラッシュしたのもSamsung系端末でした
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@210214081@21.02.14 (120400-0):1)
at com.google.android.gms.maps.internal.q.aX (com.google.android.gms.dynamite_mapsdynamite@210214081@21.02.14 (120400-0):12)
at dt.onTransact (com.google.android.gms.dynamite_mapsdynamite@210214081@21.02.14 (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 クラスの対応するメソッドに転送する必要があります。
override fun onDestroy() {
binder.mapView.onDestroy() // ←ここでmapViewがnullになってる
super.onDestroy()
}
処置
こちらに関しては明確な解決策が見つけられず、とりあえず例外をキャッチすることにしました↓
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系の古めのデバイスで出るようです)
まとめ
ということで、Androidのクラッシュレポートに対して自分なりに対策を入れてみました
入力フォームやマップあたりがやや不安定な印象ではありますね
一旦これで様子をみてみようかと思います