本稿では、Androidがどのようにしてアプリケーションを無応答状態(以降、これをANRと呼びます)と判断するか、ANRの原因、アプリケーションの高応答性を実現するためのガイドラインについて述べます。高応答性を実現するためには、効率的なAndroidコードを書くにはのページで述べたものに加え、いくつか実践すべきものがあります。本論に入る前に、アプリケーションが応答していないと判断された際に表示されるダイアログボックスの画面ショットを掲載しておきます。
Androidにおいては、Activity ManagerとWindow Managerシステムサービスによりアプリケーションの応答性がモニタされます。以下の2つのうちいずれかの状況が発生した際にANRダイアログを表示します。
ANRの定義は前述の通りで、まずはこの状況が発生する原因について検討し、次にANRを回避出来るアプリケーション構造について述べます。
Androidアプリケーションは通常、全てシングルスレッド(つまり、mainです)で動作します。つまりメインスレッドにおいて時間のかかる処理を行うことにより入力イベント処理やIntent受信を行えずANRダイアログ表示を引き起こすこととなります。
つまり、メインスレッドでの処理は最小限にとどめるべきです。特に、アクティビティにおいてはonCreate()やonResume()といったライフサイクル管理メソッドを極力コンパクトにすべきで、長時間実行されうるネットワーク操作・データベース操作や、ビットマップのリサイズといった計算時間のかかるものについては、サブスレッド内で実行すべきです(あるいはデータベースアクセスについては非同期リクエストを利用する方法もあります)。しかし実処理をサブスレッドに回しても、メインスレッドがその処理終了まで処理ブロックをかけては意味がありません。Thread.wait()やThread.sleep()でサブスレッドの処理終了を待つのではなく、メインスレッド側でサブスレッドから完了通知を受け取るためのHandlerを用意するのが良いでしょう。これを念頭に置いてアプリケーション設計を行うことで、入力受付が停止することが無く結果的に5秒間の入力イベントタイムアウトによるANRダイアログ表示を防止することが出来ます。この方式はディスプレイUIについても同様に適用することが出来ます。
IntentReceivier実行時間制約の背後には、IntentReceiver自体の利用思想があります。つまりこれは設定の保存や通知アイコン設定といった少量のデータを処理・やり取りするためのものです[※1] 。そのためメインスレッドと同様、IntentReceiverの中で長時間かかり得る処理や計算量の大きな処理を行うべきではありません。Intentを受信して長時間かかり得る処理を行う必要がある場合は、子スレッドを作成するよりもServiceを作成し、Service内で処理を実行すべきです。ちなみに、IntentReceiverからActivityを開始するのも避けるべきです。これは、開始されたActivityにより新たな画面が開かれ、ユーザが元々実行していたアプリケーションからフォーカスを奪ってしまうためです。Intentを受信した際にユーザに対して表示を行う必要がある場合はNotification Managerを利用して下さい。
通常、ユーザはアプリケーションが100ミリ秒~200ミリ秒反応しない場合にもっさり感を受けます。このため、以下ではANRを避けるために行うべきことの範囲を超え、ユーザにアプリケーションの応答性を良いと感じてもらうためのポイントをまとめます。