Androidのタスクとプロセスの話。
あるいは、「Androidのstatic変数は勝手にクリアされる」という迷信についての話。
タスク
Tasks and Back Stack | Android Developers
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack), in the order in which each activity is opened.
Androidでは、ユーザーが特定の作業を行うときに、相互作用を持つActivity
の集まりをタスクと定義しています。これはバックスタックと呼ばれる、Activity
を起動した順序で保持したスタック構造で管理されます。
タスクはいつ始まるのか
ホーム画面もまたActivity
です。
- タスクとはいつ始まるのか
- タスクを管理しているのは何なのか
と疑問に思うはずです。
You can set up an activity as the entry point for a task by giving it an intent filter with "android.intent.action.MAIN" as the specified action and "android.intent.category.LAUNCHER" as the specified category.
タスクのエントリポイントとなるのは、お馴染みandroid.intent.action.MAIN
とandroid.intent.category.LAUNCHE
のIntent
です。ランチャーから起動されたActivity
はタスクのルートになります。
そして、現在のタスクはActivityManager
によって管理されます。
Android4.Xの「最近使ったアプリ」に表示されるものがタスクだという認識でおおよそ問題ないはずです。
タスクはいつ終わるのか
If the user leaves a task for a long time, the system clears the task of all activities except the root activity. When the user returns to the task again, only the root activity is restored.
ユーザーが一定時間タスクを離れた場合、システムは自動的にタスクをクリアし、再びルートのActivity
を起動します。
alwaysRetainTaskState
フラグを使うことで、この自動的にクリアする挙動を防げます。逆に、clearTaskOnLaunch
フラグを使うことでランチャーからアプリを起動した場合に必ずタスクをクリアすることができます。
また押さえるべきポイントとして、タスクのクリアとキルは別物という点があります。「最近使ったアプリ」からタスクを消去した場合、そのアプリのプロセス(後述)も終了しますが、タスクがクリアされてもアプリのプロセスが保持される ことがあります。
タスクは複数のアプリケーションで構成される
タスクという概念が抽象化しているのは、その名の通り「ユーザーの作業」です。それは複数のアプリケーションで構成されることがあります。
例えば、写真を撮影し、それを編集し、メールで投稿する場合を考えてみると、
- カメラアプリ
- 画像加工アプリ
- メーラーアプリ
の3つのアプリケーションのActivityを横断することがあります。
もし他アプリケーションの呼び出しが「ユーザーの一連の作業の一部」であるならば、単純にstartAcitvity()
で呼び出します。
そうでない場合、他アプリケーションのタスクを明示的に開始することを示すためにFLAG_ACTIVITY_NEW_TASK
のフラグを利用します。
プロセス
Processes and Threads | Android Developers
When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution.
実行中の他のコンポーネントのない、アプリケーションのコンポートネントが起動された場合、Androidのシステムは単一の実行スレッドを持つLinuxプロセスを開始します。
タスクがActivity
の集合であり、複数のアプリケーションを含めることができたのに対して、プロセスはアプリケーション単位で一つだけ存在します。
does not have any other components running という点も重要です。通常、実行中のアプリケーションのコンポーネントはプロセスを共有します。
複数のプロセスを持つアプリも作れますがそれはどうでもいいので省略。
Androidのstatic変数が消えるとき
最近のAndroid端末はスペックが良くなったのでこの現象に遭遇することもなくなりましたが、古い記事を見ると「良く分からないけどAndroidのstatic変数は勝手に消えるから使うべきではない」みたいな情報があります。
これは正しくは、「タスクは生きているが、プロセスが死んだ」状態で発生します。
他のアプリケーションがメモリに負荷をかけるなどした場合、Androidシステムは自動的に余計なプロセスをkillします。一方、ActivityManager
がタスクをクリアする条件は「ユーザーがタスクを離れてから一定時間経過した場合」なので、killされたプロセスのActivity
を含む中途のタスクが、ActivityManager
に残るケースがあります。
このとき、タスクを再開するとそのアプリケーションのプロセスが新規に開始され、「static変数がクリアされた状態」となります。
解放されたstatic変数のオブジェクトにアクセスして、NullPointerException
というのが出来の悪いアプリでよくあるバグです。ほとんどの場合、原因はルートのActivity
で初期化処理をやっているせいです。初期化処理は適切な場所で行いましょう。
よくある勘違い
「Activityを保持しない」で、メモリがない状況を再現できる
あのオプションはActivity
を強制的にfinish()
しているだけなので、アプリケーションのプロセス消失を再現することはできません。
タスクキラーを使えば、長時間放置した状態を再現できる
タスクキラーの種類にもよります。
プロセスを殺すタイプだとタスクも消去されます。メモリを大量に確保するタイプのものを使いましょう。