すべてのパフォーマンステストに成功するコードを書くことは言うまでもなく可能なことですが、そうであっても依然としてユーザがアプリケーションを使おうとしているときに、激烈に怒ってしまうことがあります。それらは、のろのろしていたり、結構な時間ハングアップやフリーズを起こしたり、入力処理に異常な時間を費やしたりといった、まったく反応の悪いアプリケーションのことです。
Android においては、ある一定の期間に応答性に欠けるアプリケーションに対してユーザに向けてダイアログを表示するというシステム的にガードをしていて、それは Application Not Responding ( ANR : アプリケーションからの応答がない ) ダイアログと呼ばれています。ユーザはアプリケーションの継続を選択することができますが、ユーザがアプリケーションを使うたびにこのダイアログが表示されるようだとユーザの評価は下がります。ゆえに、アプリケーションに反応が良くなる設計を施すことは重要で、そうすることによりシステムから ANR がユーザに表示されることは絶対になくなります。
ほとんどの場合、アプリケーションがユーザ入力に応答しないと、システムが ANR を表示します。例えば、アプリケーションがなんらかの I/O 操作をブロックした場合 ( よくあるのはネットワークアクセスですが ) 、メインのアプリケーションスレッドが入ってくるユーザ入力を処理できなくなります。しばらくすると、システムはアプリケーションがハングアップしたと断定し、ユーザにそれを強制終了させる選択肢を提示するために ANR を表示します。
同様に、アプリケーションが手の込んだメモリー内での構成を構築したり、ことによってはゲームで次の動作の計算したりするのに時間をかけ過ぎた場合、システムはアプリケーションがハングアップしたと断定することになります。前述のテクニックを使って、それらの計算を効率的に行うことを常に確認することが重要であるとはいえ、もっとも効率的なコードはやはり実行に時間がかかります。
これらの両方のケースにおいては、子のスレッドを作成するのが普通の修正方法で、ほとんどはその対処を行うことになります。これによりメインのスレッド ( これはユーザインターフェイスのイベントループを動かします ) は実行し続け、システムがコードが固まってしまったと断定しないようにします。そのようなスレッド化は通常クラスレベルで完結することから、反応の良さはクラスの問題としてとらえることができます ( 前述のメソッドレベルの関心事である基本的なパフォーマンスの概念と比較して ) 。
ユーザに表示される ANR ダイアログ
このドキュメントでは、Android システムがアプリケーションの応答がないと判断する方法を解説し、アプリケーションが反応が良いということを保証するためのガイドラインを提供します。
このドキュメントでは以下のトピックスを取り上げます。
ANR の引き金となるものは ?
Android では、アプリケーションのレスポンスがシステムサービスであるアクティビティ管理とウィンドウ管理により監視されています。Android は以下のいずれかの条件を感知したときに、特定のアプリケーションに対して ANR ダイアログを表示します。
- 入力イベントへの応答が 5 秒以内に返らない ( 例、キー押下、スクリーンタッチ )
BroadcastReceiverが 10 秒以内に実行を完了しなかった
ANR を避ける方法
ANR の上記の定義を頭に入れて、Android アプリケーションでなぜこれが発生するのか、ANR を避けるベストなアプリケーションのつくりにするにはどうすればいいのかについて検討していきましょう。
Android アプリケーションは通常はまったくのシングルスレッド ( つまりメインスレッド ) で動いています。これは、処理が完了するのに時間のかかるメインスレッドでなんらかの動作をしているアプリケーションが、ANR ダイアログの引き金となってしまう、ということを意味しており、それはアプリケーション自体に入力イベントやインテントのブロードキャストをハンドリングするチャンスが与えられていない、ということがその原因です。
したがって、メインスレッドで実行するメソッドはどれも、できる限り小さな処理で動作させる必要があります。特にアクティビティでは、onCreate() や
onResume() といった主要なライフサイクルメソッドにおいて、できる限り小さいセットアップ処理を動かす必要があります。 ネットワークやデータベースのような潜在的に実行に長い時間を要する操作や、ビットマップのリサイズのようにコンピュータ的にも負担となる計算は、子のスレッドで動作させる必要があります ( また、データベースの操作の場合は、非同期の要求を通して行います ) 。しかしながら、これは子のスレッドが完了するのを待つ間、メインスレッドがブロックすべきであるという意味ではありません。ましてや Thread.wait() や
Thread.sleep() を呼び出すべきであるという意味でもありません。子のスレッドが完了するまで待つのではなく、メインスレッドが子スレッド用に完了通知を返す Handler を提供するようにします。アプリケーションをこの方法で設計することにより、メインスレッドは入力に対する応答性を維持することができ、それにより 5 秒の入力イベントタイムアウトが原因で起こる ANR ダイアログを回避することができます。UI を表示するスレッドは同じようにタイムアウトによる影響を受けやすいことから、それらの他のどのスレッドでもこういった手法を守るべきです。
インテントレシーバの実行時間には特別な制限があり、設定の保存や通知の登録のような、小さく、バックグラウンドでの分離した作業量の動作を行うことを意図していることが重視されています。 よって、メインスレッドで呼び出される他のメソッドと同じように、アプリケーションが潜在的に長い実行時間の操作や計算を BroadcastReceiver で行うのは避けるべきです。しかし ( BroadcastReceiver の命は短いからといって ) 、子のスレッドを介して徹底してタスクを動かすのではなく、インテントのブロードキャストに応答するのに潜在的に長い動作時間が必要な場合は、アプリケーションで Service を開始すべきです。ひとつ注意しますが、インテントレシーバからアクティビティを開始すると、ユーザが現在実行中のアプリケーションからフォーカスを奪う新しい画面が生成されてしまうので、それは避けてください。アプリケーションがインテントのブロードキャストに対する応答をユーザに示すのであれば、NotificationManager を使ってそれを行うべきです。
レスポンスの強化
ユーザは一般に、待ちが 100 から 200 ms を超えてしまうとアプリケーションが遅くなった ( また言うなれば "サクサク感" がなくなった) と感じます。 それらを踏まえ、ここではアプリケーションがユーザに反応が良いという印象を与えられるように ANR を回避するためにすべきことをさらに越えた追加のヒントをいくつか挙げておきます。
- アプリケーションがユーザ入力に応える動作をバックグラウンドで行っている場合は、その進捗状況を表示してください (
ProgressBarとProgressDialogはこれに役立ちます ) 。 - ゲームでは特に、移動の計算は子のスレッドで行ってください。
- アプリケーションが初期のセットアップ段階で時間がかかるようであれば、スプラッシュ画面の表示や、なるべく迅速にメインビューをレンダリングし、非同期で情報を集めるようにすることを考えてください。いずれのケースでも、ユーザにアプリケーションが固まってしまったと思われないように、なんらかの進捗状況を表示する必要があります。