2015年8月20日
Androidのアプリでは、UIの画面一枚をActivityと呼ぶ。
操作に応じて、様々なアクティビティーを切り換えながら、各種機能を操作するようになっていることが多い。
ところが、別のアクティビティーに表示を切り替える方法を間違えると、表示履歴のスタックをどんどん積み上げてしまい、非効率な動作となってしまうことがある。
あるアクティビティーが表示された状態で、次のアクティビティーを表示するには、例えばstartActivity()で別のアクティビティーを起動させる。
startActivity()を使うと、新しいアクティビティーのインスタンスを作って、表示履歴のスタックの先頭に積み上げる。
このアクティビティーを積み上げながら、表示するアクティビティーを切り換えていく様子は、以下のページで詳しく解説されている。
http://developer.android.com/guide/components/tasks-and-back-stack.htmlを読んで勉強しよう。
Androidでは、アプリケーションごとに、開いていったアクティビティーをスタックに積んで管理している。
アクティビティーを積んでいくスタックを、タスクという。
ホーム画面やアプリ一覧から、アプリケーションのアイコンが操作されて、アプリが最初に起動するときは、アプリのマニフェストで指定した、最初のアクティビティーが作成されて表示される。表示されるついでに、そのアプリのスタックのトップにそのアクティビティーが入る。
あとは、次のアクティビティーをstartActivity()で起動すると、前のアクティビティーをstopさせて、状態を保存してから、次のアクティビティーをcreateし、スタックのトップに追加する。
Backボタンを押すと、てっぺんのアクティビティーがdestroyされて内容が破棄され、最初のアクティビティーがスタックのてっぺんとなり、再度表示される。
Backボタンを押さずに、最初のアクティビティーのクラス名で再度startActivity()をやると、さらに最初のアクティビティーのインスタンスがスタックに積まれる。最初のインスタンスと、新しいインスタンスは別々にスタックに積まれる。例えば、クラスA→クラスB→クラスAの順でアクティビティーを起動すると、スタックにはAが二つ入る。Backを2回押せば、最初のAに戻れる。
機能間の横移動や、前のメニューに戻る操作でstartActivity()を使うと、スタックにアクティビティーがどんどん積み上げられてしまう。
上の階層に戻ったつもりが、さらにアクティビティーを掘り下げてしまっているのだ。
アクティビティーのスタックを一段戻したいときは、以下に示すいくつかの方法を使う必要がある。
startActivityForResult()とfinish()
入力の操作を行ったら、元のアクティビティーに帰ってきてほしいときなどは、入力画面のアクティビティーを「入力してほしい。終わったら終了コードを返してほしい」というように指定して、起動することもできる。
方法としては、呼び出し元と呼び出し先で、以下のようにする。
呼び出し元
startActivityForResult()で、入力操作用のアクティビティーを起動する。
このとき、リクエストコードをつけておくことがポイントだ。
入力操作用のアクティビティーが終了すると、onActivityResult()が、onResume()の後くらいに、自動的に呼び出される。これにより、入力操作用のアクティビティーが、処理を終えたことが分かる。
onActivityResult()は、リクエストコードを引数に持つので、どのリクエストに対して子アクティビティーが終わったかを知ることができる。
リザルトコードも帰ってくるので、子アクティビティーの操作が完了したのか、そうでなかったかも分かる。
呼び出し先
ふつう、startActivityForResult()で起動したアクティビティーは、処理を終えたら終了コードをセットしたのち、finish()により終了する。
こうすることで、そのアクティビティーはスタックのトップから削除され、呼び出し元のアクティビティーがスタックの新たなトップとなる。
呼び出し元のアクティビティーのインスタンスをもう1つ作るわけではなく、スタックを1件巻き戻すだけとなるところがポイントだ。
setResult()を実行すれば、呼び出し元のアクティビティーに対して、リターンコードを記録できる。
さらに、入力画面で入力したことなどを、Intentにセットして、呼び出し元のアクティビティーに送ることもできる。
setResult()で結果をセットし終えたら、finish()を実行して、アクティビティーを閉じる。
ちなみに、アクティビティーが生成されてから一度もsetResult()を実行せずに、finish()したり、戻るボタンを押したりした場合は、結果にはRESULT_CANCELEDが自動的にセットされるらしい。勝手にRESULT_OKが入ることはないらしい。ただし、過去に作ったアクティビティーのインスタンスを使い回すと、何が入っているのか分からないので、入力窓などのアクティビティーは、毎回新規作成したほうがよいだろう。
Backボタンでアクティビティーを降ろしていく
Backボタンを押すと、そのアプリでタスクのてっぺんにある、表示中のアクティビティーがスタックから破棄されて、スタックの1つ下のアクティビティーを、新たなスタックのトップとして表示する。
これにより、画面表示が、スタックとして1つ手前のアクティビティーに戻る。
スタックに入っている最後のアクティビティーを、Backボタンで閉じてしまうと、そのスタックからはアクティビティーが全てなくなり、タスクが終わってしまう。画面表示はホーム画面に戻る。
Backボタンを押し続けて、タスクからアクティビティーがなくなった状態で、そのアプリケーションを、ホーム画面のアイコンで再度選び直すと、アプリが再起動して、そのアプリとして初期に起動するアクティビティーが表示される。
Backボタンを押して、スタックにアクティビティーが全てなくなった状態でも、タスク一覧ボタンを押すと、そのアクティビティーが表示される。
そのアプリをタスク一覧から選択すると、アプリを最初に起動したときのアクティビティーが表示される場合と、アクティビティーの表示が出ず、何も起こらず画面が閉じてホーム画面に戻ってしまう場合があるようだ。動作に再現性がない。
本来は、Backボタンを押し続けて、タスクを空にしたアクティビティーに対して、タスクを切り換えるというのは、行うべきでない操作なのかもしれない。
Backボタンでアプリのスタックを空にしてしまったときは、アプリのアイコンを押して、アプリを再起動するのが正しい操作ということなのだろう。
一本のアプリは基本的に、一つのアクティビティーのスタック(タスク)に、アクティビティーを積んだり降ろしたりしながら、画面遷移を制御する。
Androidは「マルチタスク」なので、そのようなアクティビティーのスタックを、同時に複数持ち、切り換えながら利用できる。
アプリの動作中にHOMEボタンを押すと、画面はホーム画面に切り替わるが、そのアプリのタスクのスタックはそのままになっている。
従って、そのアプリのタスクは、そのアプリがどのようなアクティビティーを起動してきたかの履歴を、スタックとして覚えている。
アプリのアイコンを再度押すと、そのアプリの動作中のタスクに画面が切り替わる。具体的には、そのタスクのスタックのてっぺんにあるアクティビティーが再度表示される。
他のアプリのアイコンを押すと、そのアプリが新しく立ち上がり、以前使っていたアプリとは別に、アクティビティーのスタックを作る。そして、そのアプリの最初のアクティビティーが表示される。
タスク一覧ボタンを押すと、既に起動しているアプリが一覧で表示される。
タスク一覧に表示される縮小画面はそれぞれ、各タスクでスタックのてっぺんにあるアクティビティーの表示内容を示している。
タスク一覧表示から、アプリケーションを選ぶと、選ばれたアプリケーションのタスクが持つスタックの、てっぺんのアクティビティーに画面が切り替わる。
ホーム画面などに配置される、アプリを起動するためのアイコンは、アプリが起動中かに応じて、押した場合の動作が異なる。
アプリが起動していないとき(タスクのスタックが空のとき)
アプリがまだ起動していないか、アプリのスタックがBackボタンを押し続けるなどして空になっているときは、アプリのアイコンを押すと、アプリごとに設定されている初期のアクティビティーが表示される。
あらためて、アプリを起動するような感じになる。
アプリが既に起動しているとき(タスクのスタックに何か入っているとき)
アプリのアイコンを押すと、そのアプリのスタックに既に入っているアクティビティーのうち、てっぺんのアクティビティーが表示される。
既に使っている途中のアプリに戻り、操作を続けるような感じになる。
こんなふうに、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.