マルチタスキング、Androidのやり方

android XMLで有名なTim BrayがGoogleに移り、若干放置気味だったAndroidの開発者向けBlogも更新され始めましたね。

iPhone OS 4.0でマルチタスキングに対応するのもあってか、Multitasking the Android Wayという、なかなか興味深い記事がありました。この記事では、Androidのマルチタスキングの設計根拠などが簡単に述べられています。

ソースが公開されていると言っても、なぜそういう設計になっているのか、わからないことが多いですよね。おそらくこうだろうと自分で推測して納得するわけですが、実際のところ、何だかすっきりしません。この記事みたいに、フレームワーク設計者による設計背景が垣間見える記事はいいですね。

以下、ちょっと長いですがざっと訳してみました。


マルチタスキング、Androidのやり方

Posted by Tim Bray on 28 April 2010 at 11:41 AM
[Androidのすべてのほぼ中心にいるソフトウェアエンジニア、Dianne Hackbornによる記事 — Tim Bray]

マルチタスキング、Androidのやり方

Androidはかなり独特のやり方で、同時に複数のアプリを動すことができる。別のプラットフォームからやって来た開発者は、そのやり方に驚くかもしれない。この動作を理解しておくことは、Androidプラットフォームとシームレスに統合された、快適に動作するアプリケーションを設計する上で重要だ。この記事では、Androidのマルチタスキング設計根拠、アプリケーション動作への影響、Android特有の機能を最大限に活用する方法について説明する。

設計検討

モバイルデバイスには、デスクトップやWebには存在しない技術的制約とユーザ体験要求がある。以下が、Androidのマルチタスキングを設計したときに課した4つの重要な制約だ。

  • アプリで作業を「終えた」とき、ユーザにアプリをクローズさせたくない。モバイル環境では、一日のうちに様々なアプリを繰り返し、短時間だけ使うため、これはうまくいかないためだ。
  • モバイルデバイスには潤沢なスワップ空間がない。したがって、メモリ使用はかなり厳しく制限される。これについては、Robert Loveがすばらしい記事を書いている。
  • モバイルデバイスにおけるアプリ切り替えは極めて重要だ。私たちの目標は、新しいアプリを1秒未満で立ち上げることだ。ユーザが複数のアプリを切り替えて使うとき、これは特に重要になる。例えば、ビデオを見ているときに、アプリを切り替えて新規SMSメッセージを読んで、それからまたビデオに戻ってくるといったケースだ。待ちが長いと、すぐにユーザに嫌われてしまうだろう。
  • 「すべてのアプリは同等に作られる」というフィロソフィーに基づき、ビルトインGoogleアプリを書くのに十分なAPIを利用可能しなければならない。つまり、バックグラウンド音楽再生、データ同期、GPSナビゲーション、アプリダウンロードなどの機能は、サードパーティ開発者が使えるのと同じAPIで実現しなければならない。

最初の2つの要件は、なかなか興味深い競合だ。私たちは、ユーザがアプリのクローズを気にしなくてもいいようにしたい。むしろユーザには、すべてのアプリが常に動いているように見せたい。だがそれと同時に、モバイルデバイスでは使えるメモリに厳しい制限がある。利用可能なメモリ量以上にRAMを要求すると、性能が低下したり、すぐにシステムがエラーを起こすようになる。一方、スワップのあるデスクトップでは、スワップ空間へのページングが必要になってくると、すぐに遅くなり始める。こうした競合する制約が、Androidにおけるマルチタスキング設計の重要な動機となった。

いつアプリケーションを「止める」のか?

Androidのマルチタスキングでよく誤解されているのは、プロセスとアプリケーションの違いだ。Androidの場合、プロセスとアプリケーションは密結合していない。アプリを動かす実プロセスがなくても、ユーザにはアプリが存在しているように見えるかもしれない。複数のアプリがひとつのプロセスを共有しているかもしれないし、ひとつのアプリが必要に応じて複数のプロセスを利用しているかもしれない。アプリがアクティブでなく何もしていないときも、アプリのプロセスはAndroidによって保持されているかもしれない。

アプリケーションのプロセスが「動いている」ように見えるからといって、実際にアプリケーションが動いたり、何かしているわけではない。プロセスがあるのは単に、いつかまた必要になるときに備えて、保持しておくのが最善だとAndroidが判断したためかもしれない。また、そのアプリケーションからちょっと離れて、後でまた同じところに戻ってくるかもしれない。その間、Andoridは他のことをやるためにプロセスを始末する必要があるかもしれない。

このようなAndroidのアプリケーション処理において覚えておくべき重要なことは、プロセスはきれいにシャットダウンされないということだ。ユーザがアプリから離れると、そのプロセスはバックグラウンドに置かれる。必要であればそのまま動き続けることができ(例えばWebページのダウンロード)、ユーザがアプリに戻ってくると、即座にフォアグラウンドへと戻される。デバイスがメモリを使い果たしていなければ、Androidはこうしたプロセスをすべて保持して、実際にすべてのアプリケーションが常に「動いている」状態を維持するだろう。

でももちろん、メモリには制限がある。そのため、Androidはいつ必要のないプロセスを始末するか判断しなければならない。これがAndroidのプロセスライフサイクルをもたらす。各プロセスがどれだけ重要か判断し、落とすべき次のプロセスを決定するのだ。そのルールは、現在のユーザ体験にとってそのプロセスがどれだけ重要であるかと、ユーザが最後にそのプロセスを必要としてからどれくらい時間が経過したかに基づいている。

いったんプロセスを削除する必要があるとAndroidが判断すると、それは容赦なく行われる。つまり、単にプロセスを強制終了する。これによって、カーネルはそのプロセスが使っていたすべてのリソースをすぐに再利用することができる。アプリケーションがうまく書かれていることや、行儀よく終了要求に反応することに頼る必要はない。カーネルがすぐにアプリケーションリソースを再利用できるおかげで、深刻なメモリ枯渇の発生を防ぐのが簡単になる。

「すべてのアプリケーションが常に動いている」という体験を維持するため、ユーザが後から強制終了されたアプリに戻っても、Androidは何とかしてユーザが最後に見ていたのと同じ状態で再起動する必要がある。これは、ユーザがアプリケーションだと認知している部分(アクティビティ)を記録しておき、ユーザが見ていた最終状態に関する情報に基づいて再起動することによって実現される。この最終状態は強制終了されたときではなく、ユーザがアプリケーションのその部分を離れるたびに作られる。したがって、アプリケーションがその時点で正常に反応しているかによらず、カーネルは自由にアプリを強制終了することができる。

ある意味、Androidのプロセス管理はスワップ空間の一種のように見える。すなわち、アプリケーションプロセスが、使用中のメモリ領域を表す。メモリが少なくなると、いくつかのプロセスが強制終了させられる(スワップアウト)。そして、そのプロセスが再び必要になると、最後に保存した状態から再起動(スワップイン)されることになる。

明示的なバックグラウンド実行

これまでは、所定のメモリ管理に基づきAndroidがプロセスを強制終了しない限り、暗黙のうちにアプリをバックグラウンドで動作させるというやり方について説明した。これはバックグラウンドでWebページをダウンロードするといった作業にはうまくいく。しかし、もっと厳しい要件のある機能の場合にはどうだろうか? 例えば、バックグラウンド音楽再生、データ同期、位置検出、アラームクロックといったものだ。

これらのタスクでは、アプリケーションはAndroidに「この時点で明示的に実行したい」と教える必要がある。これを可能にする仕組みには大きく2つある。この2種類のコンポーネント、ブロードキャストレシーバとサービスはマニフェストに書くことで利用することができる。

ブロードキャストレシーバ

BroadcastReceiverを使うと、アプリケーションを短期間、バックグラウンドで、何かが発生した結果として動かすことができる。ブロードキャストレシーバはさらに高度な仕組みを実現するために、いろいろなところで使われている。例えば、AlarmManagerを使うと、アプリケーションは将来のある時刻にブロードキャストを送ることができる。LocationManagerを使うと、関心のある位置変化を検出したときにブロードキャストを送ることができる。レシーバに関する情報はアプリケーションのマニフェストの一部なので、たとえアプリケーションが動いていなくても、Androidはそれを見つけて起動することができる。もちろん、すでにそのプロセスがバックグラウンドにあれば、ブロードキャストは非常に効率よく直接そのプロセスにディスパッチされる。

アプリケーションはブロードキャストを処理するのに一定の時間が与えられる(現在10秒)。もしその時間内に処理が完了しなければ、アプリケーションは何かおかしいと見なされ、そのプロセスは即座にバックグラウンド状態に置かれ、必要に応じて、メモリを空けるために強制終了させられる。

ブロードキャストレシーバは外部の刺激に反応して、ちょっとした処理をするのに最適だ。例えば、新しいGPS位置情報が通知されたときに、それをユーザに通知するといったものだ。これは非常に軽い。なぜなら、そのアプリケーションのプロセスはブロードキャストをアクティブに受信している間だけ存在すればよいためだ。アクティブなのは一定の時間だけなので、実行中にプロセスが強制終了されないことを、かなり強く保証することができる。しかし、このやり方はネットワーク通信のような時間が不定なものには適していない。

サービス

サービスを使うと、アプリケーションは長時間動作するバックグラウンド操作を実現することができる。実際にはサービスは他にもたくさんの機能を提供しているが、ここでは、サービスの基本的な目的は、アプリケーションが「やあ、僕がもういいよと言うまで、バックグラウンドでいいから動き続けたいんだ」と言うことだ。アプリケーションは、サービスを明示的に起動停止することで、サービスの実行タイミングを制御する。

サービスはリッチなクライアント-サーバモデルを提供するが、どう利用するかは自由だ。アプリケーションのサービスを開始すると、Androidはアプリケーションのプロセスで、そのコンテキストを提供するコンポーネントをインスタンス化するだけだ。その後、それをどのように使うかはアプリケーション次第だ。必要なコードをすべてサービス本体に入れて、アプリケーションの別の部分とやり取りしないようにしてもよい。アプリの別の部分とシングルトンオブジェクトを共有して、必要に応じてどこからでも直接サービスのインスタンスが取得できるようにしてもよい。また、もし望むのであれば、別のプロセスで実行して、本格的なRPCプロトコルを使ってやり取りしてもよい。

サービスのプロセス管理はブロードキャストレシーバとは違う。なぜなら、サービスの数は無制限で、未知の期間、動かすよう指示できるためだ。要求されたすべてのサービスを実行できるだけの十分なRAMがないかもしれない。したがって、サービスが実行し続けられるか、あまり強い保証はない。

あまりにRAMが少ないと、サービスをホストするプロセスはバックグラウンドプロセスと同様、即座に強制終了させられるだろう。しかし、適切な場合には、Androidはこれらのサービスが実行し続けたかったことを覚えており、後でRAMが利用可能になったときにプロセスを再起動してくれる。例えば、ユーザがRAMを大量に必要とするWebページを開くと、ブラウザのメモリ要求が落ち着くまで、Androidは同期などのバックグラウンドのサービスプロセスを強制終了するかもしれない。

サービスを「フォアグラウンド」と見なすよう要求することで、サービスはこの動作をネゴシエーションできる。これにより、サービスは「強制終了しないでくれ」という状態になるが、ユーザに対してサービスがアクティブに動いていることを通知する必要がある。これはバックグラウンド音楽再生やカーナビゲーションといったサービスには有用だ。サービスがアクティブなことが、ユーザにとって積極的にわかるようになる。音楽を再生しながらブラウザで閲覧しているときには、必ずステータスバーに音楽再生マークが現れる。Androidはこうしたサービスを強制終了しようとはしないが、そのトレードオフとして、ユーザはサービスが動いていることを知り、望むときに明示的に停止することができる。

汎用コンポーネントの価値

Androidのブロードキャストレシーバとサービスという汎用コンポーネントを使うと、開発者はバラエティに富んだ効果的なバックグラウンド操作を実現することができる。これらを使うと、もともとまったく想定されていなかったこともできるようになる。Android 1.0では、ビルトインおよびプロプライエタリなGoogleアプリが提供しているバックグラウンド動作のほぼすべてが、これらの仕組みを使って実装されている。

  • 音楽再生はサービスとして動いている。これにより、ユーザが音楽アプリから離れても、再生し続けることができる。
  • アラームクロックは、アラームマネージャを使ってブロードキャストレシーバを予約することで、次回のアラーム時刻でアラームを鳴らしている。
  • カレンダーアプリも同じように、アラームを予約することで、次回のカレンダーイベントに適した時刻に通知を表示、更新している。
  • バックグラウンドのファイルダウンロードはサービスとして実装されており、処理すべきダウンロードがあれば動くようになっている。
  • メールアプリは定期的にサービスを起動するためにアラームを予約して、新規メールを確認、取得している。
  • Googleアプリケーションはネットワークからのプッシュ通知を受信するためにサービスを管理している。そして、コンタクト同期などを実行する必要があると言われると、ブロードキャストを個々のアプリに送っている。

プラットフォームが進化するにつれ、これら基本コンポーネントは主要な新しい開発者向け機能の多くを実装するために使われてきている。

  • インプットメソッドは開発者によってサービスコンポーネントとして実装される。Androidはそのサービスを管理し連携することで、現在のIMEとして表示している。
  • アプリケーションウィジェットはブロードキャストレシーバになっており、Androidがそれとやり取りする必要があると、ブロードキャストを送る。これにより、アプリケーションのプロセスを動かしたままにする必要はなくなるので、ウィジェットを軽量にすることができる。
  • アクセシビリティ機能はサービスとして実装されている。この機能を使用中、Androidはサービスを動かし続け、ユーザのインタラクションに応じて適切な情報をサービスへと送っている。
  • Android 2.0で導入されたSyncアダプタはサービスとして実装されており、データ同期を実行する必要があるときにはバックグラウンドで実行される。
  • Live Wallpaperはサービスとして実装されており、ユーザの選択によってAndroidはサービスを開始する。