開発時には以外に気づかない、というか意識していないといった事もあると思います。
リリース後、バグとして上がってくる謎のエラー。。。
なぜシングルトンのデータが吹っ飛ぶのか。。。
もしかしたらタスクがKillされる事によって発生しているかもしれません。
タスクKillってどういう状態か?
公式サイトにのっているのですが、
Android OSの判断でメモリ開放するためにタスクを破棄するという動作が標準搭載されています。
※個人的にタスクKillと呼んでいる
その破棄された状態でアプリを起動すると、復旧する際にシングルトンで持っていたデータなどMemory毎破棄されてしまうため、NullPointerException等様々な問題が発生します。
※作りによっては上手く復旧できる場合あり
そう。。。
復旧なのです。。。
アプリが初回から始まるといった事は無く復旧。。。
詳細は本家へ!
https://developer.android.com/guide/components/tasks-and-back-stack.html
注: バックグラウンドには複数のタスクを一度に置くことができます。しかし、ユーザーが多数のバックグラウンド タスクを同時に実行すると、システムがメモリを回復するためにバックグラウンド アクティビティを破棄する場合があります。その結果、アクティビティの状態は失われます。アクティビティの状態セクションをご覧ください。
タスクがKillされた状態を作り出すとなにが良いのか?
単純にいえば時短です!
メモリ解放するタイミングがOS依存なので、色々なアプリを立ち上げて、
Killされたかな??Killされたかな??ってやる必要があります。。。
※他にも良い方法があるかもしれませんが、私は以前上の動作で対応していました。。
Kill状態を作り出すには
Killはプログラムで操作できた。。。
下記を埋め込めばKillできます。
※adbとかでもあるかもですが
val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
am.killBackgroundProcesses("ここにKillしたいアプリのpackageを入れる")
なので、特定のアプリタスクをKillするアプリを別で作って簡単にテストが行えるようにしました。
対処方法
対処方法って書きましたが、結局か。。といった内容です。
既存アプリの状態が不明であれば、各画面単位でKillして発生ベースでエラー対応を行い、
まず落ちなくする事が最優先かと!
※設計が不明等よくあるので、調査よりKillしてTry And Errorが以外にはやい!
開発する際は初期の設計段階でタスクKillがある事を踏まえていたほうが良い。
ポイントとして、画面遷移のI/Oは明確に決めておいた方が良い
ActivityのIntentやFragmentのBundleはKillでは消えない!
最低限上記を対応する事で、復旧時にリソースを作り直す事になりそうだが、エラーでクラッシュよりは良いかと。
公式サイトに載っていますが、onSaveInstanceStateという物を使って必要なリソースを残しておく手段も良いです。
ただ、たくさんのデータを保存すると、エラーになったりしてしまうのでその点は要注意!
※詳細は本家に!w
※このバグにはまって苦労した。。。
https://developer.android.com/about/versions/nougat/android-7.0-changes.html
その他の重要事項
多くのプラットフォーム API は、Binder トランザクションで送信される大きなペイロードをチェックし、暗黙的にログ記録したり、削除したりするのではなく TransactionTooLargeExceptions を RuntimeExceptions として再度スローするようになりました。一般的な例としては、Activity.onSaveInstanceState() で大量のデータを格納することです。これにより、アプリが Android 7.0 をターゲットにしている場合は、ActivityThread.StopInfo で RuntimeException がスローされます。