2011年1月12日水曜日

アクティビティとタスク (Android)

 

開発の基礎」の「アクティビティとタスク」の節がなかなか理解できなかったのでまとめ。

タスク

アクティビティのスタック。ブラウザでいえばHistoryみたいなもの。タスクそれぞれにアクティビティのスタックがある。

タスクはブラウザのタブみたいなもの。ただし、タブのように "見える化" されていなくてタスクに含まれるアプリケーションを再び起動しようとしたときにそのアプリケーションのタスクがフォアグラウンドになる。ただし、インテントのflagプロパティやアクティビティの起動モード定義によって挙動は変わる。

Affinity (親和性?)

アクティビティは自身と同じAffinityが設定されているタスクに所属しようとする。

タスクのAffinityはルートのアクティビティによって決まる。

アクティビティのAffinityはAndroidManifest.xmlのactivity要素のtaskAffinity属性で設定(たぶん任意の文字列で指定可能)。指定がなければ、application要素のtaskAffinity属性を継承する。アプリケーションのAffinityのデフォルトはmanifest要素のパッケージ名。

インテントにFLAG_ACTIVITY_NEW_TASK フラグが設定されている場合、新しいアクティビティは別のタスクに所属しようとするが、そのアクティビティと同じAffinityが設定されている既存のタスクがあればそこに追加される。なければ新しいタスクが開始される。

[あとで検証] もしFLAG_ACTIVITY_NEW_TASK フラグを設定したインテントで新しいアクティビティを起動するとき、startActivity()を呼び出したタスクと新しいアクティビティに同じAffinityが設定されていたら新しいタスクが起動するかどうか。たぶん新しいタスクはできないと予想。

activity要素に allowTaskReparenting="true" と設定されていると、このアクティビティと同じAffinityが設定されているタスクがフォアグラウンドに移ったときに、アクティビティを開始したタスクからそのタスクに移動できる。たとえば taskAffinity="A" のタスクで taskAffinity="B" のアクティビティを開始すると B は A のタスクに属するが、taskAffinity="B" のアプリケーションを起動すると B のアクティビティがこのタスクに移動する。

起動モード

activity要素の launchMode属性で定義。

"standard"

  • インテントを開始した(startActivity() を呼び出した)タスクに保持される。
  • 複数回インスタンス化できる。
  • 新しいインテントに応答するときには必ず新しいインスタンスが作成される。

"singleTop"

  • インテントを開始した(startActivity() を呼び出した)タスクに保持される。
  • 複数回インスタンス化できる。
  • 既存のインスタンスがスタックの最上位にあれば再利用して新しいインテントを処理する。最上位にない場合は新しいインスタンスが作成される。

"singleTask"

  • アクティビティが常にタスクのルート アクティビティになる。
  • アクティビティのインスタンスは1つに制限される。
  • 同じタスクに属する別のアクティビティを開始することができる。
  • このアクティビティがスタックの最上位にない場合インテントはドロップされる。インテントがドロップされたとしても、タスクがフォアグラウンドに移ったままになる。

"singleInstance"

  • アクティビティが常にタスクのルート アクティビティになる。
  • アクティビティのインスタンスは1つに制限される。
  • そのタスク内の唯一のアクティビティとして単独で動作。ここから別のアクティビティを開始した場合、そのアクティビティは別のタスクで起動する。

後述するが、アプリケーションのメインアクティビティには singleTask が向いているはずだが、実際は上記のような挙動にならないため standard に設定しておくと良い。

スタックのクリア

ユーザーがタスクを長時間放置したときは、ルート以外のアクティビティがクリアされる。この挙動はactivity要素の属性で変更できる。

alwaysRetainTaskState = "true"

アクティビティはクリアされない。

clearTaskOnLaunch = "true"

タスクを離れるとルートを含めた全てのアクティビティがクリアされる。

finishOnTaskLaunch = "true"

タスクを離れるとこのアクティビティはクリアされる。それがルートであっても。

Intent に FLAG_ACTIVITY_CLEAR_TOP フラグを設定すると、インテントを処理するアクティビティのインスタンスが対象タスクのスタック内に存在する場合、そのインスタンスより上位(新しい方)のアクティビティはすべてクリアされる。

startActivityForResult() で呼び出したアクティビティがクリアされてしまった場合、呼び出し元アクティビティの onActivityResult() が resultCode = RESULT_CANCELED で呼ばれる。

タスクの開始

アクティビティのインテントフィルタのアクションを android.intent.action.MAIN、カテゴリを android.intent.category.LAUNCHER に設定すると、アクティビティのアイコンとラベルがアプリケーションランチャに表示され、アプリケーションのエントリポイントになる。

ドキュメントでは、この設定をしたアクティビティはユーザーが他のタスクに移動したあとでアプリケーションに戻ってこられるように、起動モード singleTask か singleInstance を設定したほうが良いとされているが、実際にやってみると起動モード singleTask ではルートアクティビティ以外がクリアされた状態で起動してしまう。なぜか起動モード standard のタスクではスタックが保持されたまま元のタスクに戻ることができた(Android 1.6, 2.2で検証)。

参考ページ

Y.A.M の 雑記帳 「Android Activity, Task, Stack, Launch mode」