アクティビティーのスタックとマルチタスク

2015年8月20日


Androidのアプリでは、UIの画面一枚をActivityと呼ぶ。

操作に応じて、様々なアクティビティーを切り換えながら、各種機能を操作するようになっていることが多い。

ところが、別のアクティビティーに表示を切り替える方法を間違えると、表示履歴のスタックをどんどん積み上げてしまい、非効率な動作となってしまうことがある。

アクティビティーの履歴はスタックに入る

あるアクティビティーが表示された状態で、次のアクティビティーを表示するには、例えばstartActivity()で別のアクティビティーを起動させる。

startActivity()を使うと、新しいアクティビティーのインスタンスを作って、表示履歴のスタックの先頭に積み上げる。

このアクティビティーを積み上げながら、表示するアクティビティーを切り換えていく様子は、以下のページで詳しく解説されている。

http://developer.android.com/guide/components/tasks-and-back-stack.htmlを読んで勉強しよう。

startActivity()だけの遷移では積みっぱなし

Androidでは、アプリケーションごとに、開いていったアクティビティーをスタックに積んで管理している。

アクティビティーを積んでいくスタックを、タスクという。

ホーム画面やアプリ一覧から、アプリケーションのアイコンが操作されて、アプリが最初に起動するときは、アプリのマニフェストで指定した、最初のアクティビティーが作成されて表示される。表示されるついでに、そのアプリのスタックのトップにそのアクティビティーが入る。

あとは、次のアクティビティーをstartActivity()で起動すると、前のアクティビティーをstopさせて、状態を保存してから、次のアクティビティーをcreateし、スタックのトップに追加する。

Backボタンを押すと、てっぺんのアクティビティーがdestroyされて内容が破棄され、最初のアクティビティーがスタックのてっぺんとなり、再度表示される。

Backボタンを押さずに、最初のアクティビティーのクラス名で再度startActivity()をやると、さらに最初のアクティビティーのインスタンスがスタックに積まれる。最初のインスタンスと、新しいインスタンスは別々にスタックに積まれる。例えば、クラスA→クラスB→クラスAの順でアクティビティーを起動すると、スタックにはAが二つ入る。Backを2回押せば、最初のAに戻れる。

機能間の横移動や、前のメニューに戻る操作でstartActivity()を使うと、スタックにアクティビティーがどんどん積み上げられてしまう。

上の階層に戻ったつもりが、さらにアクティビティーを掘り下げてしまっているのだ。

アクティビティーのスタックを一段戻したいときは、以下に示すいくつかの方法を使う必要がある。

ホームボタンやタスク一覧ボタンを押してマルチタスク

一本のアプリは基本的に、一つのアクティビティーのスタック(タスク)に、アクティビティーを積んだり降ろしたりしながら、画面遷移を制御する。

Androidは「マルチタスク」なので、そのようなアクティビティーのスタックを、同時に複数持ち、切り換えながら利用できる。

ホームボタン

アプリの動作中にHOMEボタンを押すと、画面はホーム画面に切り替わるが、そのアプリのタスクのスタックはそのままになっている。

従って、そのアプリのタスクは、そのアプリがどのようなアクティビティーを起動してきたかの履歴を、スタックとして覚えている。

アプリのアイコンを再度押すと、そのアプリの動作中のタスクに画面が切り替わる。具体的には、そのタスクのスタックのてっぺんにあるアクティビティーが再度表示される。

他のアプリのアイコンを押すと、そのアプリが新しく立ち上がり、以前使っていたアプリとは別に、アクティビティーのスタックを作る。そして、そのアプリの最初のアクティビティーが表示される。

タスク一覧ボタン

タスク一覧ボタンを押すと、既に起動しているアプリが一覧で表示される。

タスク一覧に表示される縮小画面はそれぞれ、各タスクでスタックのてっぺんにあるアクティビティーの表示内容を示している。

タスク一覧表示から、アプリケーションを選ぶと、選ばれたアプリケーションのタスクが持つスタックの、てっぺんのアクティビティーに画面が切り替わる。

アプリ起動アイコン

ホーム画面などに配置される、アプリを起動するためのアイコンは、アプリが起動中かに応じて、押した場合の動作が異なる。

独特のマルチタスク観

こんなふうに、Androidでは、ホームボタンやタスク一覧への切り替え、アプリ起動用のアイコン等を使って、タスクを対話的に切り換えて使えるので、文字通りマルチタスクということになっている。

LinuxやWindowsのように、本当に複数のプログラムが同時並行で好きなように動くというよりも、ユーザがどのタスクを使うかを、手動で切り換えられるという意味でのマルチタスクとしてとらえると、分かりやすい。

アクティビティーを多くスタックに積むと、システムがメモリから、アクティビティーのデータを捨ててしまうことがある。

メモリからデータが捨てられても、後でリジュームできるようにするために、アプリのコードで、アクティビティーの状態をセーブしたりロードしたりする処理を加えたほうがいい場合もある。

他のOSのように、メモリが足りなくなったら、適当にSWAPしてはくれない。手動でのバックアップとリストアを必要とするAndroidは、やや面倒なシステムだ。

この仕組みを入れないと、別のアプリから帰ってくると、アクティビティーの中身が消えていた、というようなことも起こりうるらしい。

テキスト入力枠など、基本的なGUI部品に対しては、システムの側でデータをフラッシュメモリ側に待避する仕組みがあるようだが、カスタムで何か作った場合は、状態を保存・復元するためのコードが必要だ。

まずは、基本のUI部品の使用を基本として、みだりにたくさんのアクティビティーをスタックに積まないことから始めたい。

その後こだわりを持って、特別なUI部品を作ったときは、データを待避する処理を実装して、デバッグモードでわざとアクティビティーのデータをメモリから消す場合を想定した処理を試して、ステータスのセーブとリストアが正しく機能できるようにしよう。

サンプルアプリケーション

Test512のアプリケーションに、startActivityForResult()やonActivityResult()、setResult()、finish()等を、試しに実装してみた。

MainActivityから、startActivityForResultで、リクエスト1番でSecondActivityを起動する。

SecondActivityでは、ボタンを押した場合に限り、RESULT_OKと、インテントにキー"text2"で、表示したいテキストを入れる。

MainActivityでは、onActivityResult()にて、リクエスト1番で、結果がRESULT_OKだった場合に限り、画面上のtext2に、戻り値でもらったインテントの"text2"から値を受け取り、画面に表示する。

こんなわけで、2つめのアクティビティーを要求コードをつけて起動し、2つめのアクティビティーで入力作業を行い、1つめのアクティビティーで結果を受け取り表示するという、一連の流れを実現できた。


杉原俊雄のホームページAndroidアプリ開発メモ(もくじ)

(c) 2013 Toshio Sugihara. All rights reserved.