Android(X06HT Desire) Activity の状態を一時的に保存する。Activity ライフサイクルの確認。
先日の勉強会で発表のために、Android アプリケーションの開発から公開までしたのだけれど、なにぶん知識と時間不足のため、ちょっと完成度低めだったので、少しずつ改良しながら、さらに野望の実現を目指す。
作成したのは、単語帳アプリケーションなのだが、単語帳のカードを途中までめくったところで端末の方向を切り替えると、最初のページに戻ってしまう。これはいただけない。修正する。
プライベートなプリファレンスをつかって、現在のページを保持させれば良いだろう。
getPreferences(MODE_PRIVATE) で、暗黙で Activity の クラス名をキーとしてgetSharedPreferencesを利用できる
プライベートなプリファレンスを使う例
@Override protected void onPause() { Editor editor = getPreferences(MODE_PRIVATE).edit(); // 現在ページを保存 editor.putInt(PREF_KEY_CURRENT_PRACTICE, vfCard.getDisplayedChild()); editor.commit(); super.onPause(); } @Override protected void onResume() { int idx = getPreferences(MODE_PRIVATE).getInt(PREF_KEY_CURRENT_PRACTICE, 0); // 現在ページを復帰 if (0 <= idx && idx < vfCard.getChildCount()) { vfCard.setDisplayedChild(idx); } super.onResume(); }
・・・ が、これがうまくいかない。
Activity のライフサイクルイベントのメソッドをオーバーライドして、ログを仕込んで、なにが起こっているか確認してみる
まずは、アプリケーションを起動し、対象の Activity を呼び出すと、
09-02 23:32:43.878: INFO/MyApp(11021): onCreate() 09-02 23:32:44.668: INFO/MyApp(11021): onStart() 09-02 23:32:44.668: INFO/MyApp(11021): onResume()
onCreate から、onStart()、onResume() を経てActivity 実行中に。ふんふん。リファレンス通り。
そして、画面を横にしてみる。
09-02 23:33:05.348: INFO/MyApp(11021): onPause() 09-02 23:33:05.348: INFO/MyApp(11021): onStop() 09-02 23:33:05.348: INFO/MyApp(11021): onDestroy() 09-02 23:33:05.428: INFO/MyApp(11021): onCreate() 09-02 23:33:06.358: INFO/MyApp(11021): onStart() 09-02 23:33:06.368: INFO/MyApp(11021): onResume()
おおっと、画面を横にした程度なら、onPause() から onResume() のコースを取ってくれても良さそうなものだが、一旦 Destroy までされてしまうようだ。
画面の縦と横では内容が全く異なるはずなので、画面自体の再構築の必要がある。当然と言えば当然か。
毎回 onCreate() も呼ばれてしまうとなると、onCreate() で初期化しようにも、Activity を新規に構築するのか、画面の向きが変わったのか判断つかない。 となると、Acitvity を呼び出すタイミングで、値をクリアする操作をしないといけない。 この場合、Activity プライベートなプリファレンスは使えないので、Activity 間で共有できるプリファレンスを使って書き換えてみる。
共有プリファレンスを使う例
@Override protected void onPause() { Editor editor = getSharedPreferences( PREF_KEY, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE).edit(); editor.putInt(PREF_KEY_CURRENT_PRACTICE, vfCard.getDisplayedChild()); editor.commit(); super.onPause(); } @Override protected void onResume() { int idx = getSharedPreferences(PREF_KEY, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE) .getInt(PREF_KEY_CURRENT_PRACTICE, 0); if (0 <= idx && idx < vfCard.getChildCount()) { vfCard.setDisplayedChild(idx); } super.onResume(); }
で、呼び出し元の Activity では、上記 onPause と同様にして、putInt() を利用して、 0 (初期値) をセットすれば、期待したように動くようになった。
よしよし。
・・・ が、ちょっとまった。プリファレンスは永続化される。 しかも自分が想定した利用法では、永続化する必要は全くない。たかだか Activity の一時的な状態を格納するだけに使うのはもったいない(?)のではないか。
Application の サブクラスを作成してそこに保持させる
ようなことを、ちらっとどこかで見たな~2chだったかな~
Activity は 破棄されても、Application は、基本生きているはず。ただ、システムによって、メモリが足りないときとかはKill されるということだったかと。リファレンス見てもそういう感じだ。
なので、Application が 不用意に Kill されても 保持していたいような設定などは、上記プリファレンスを使うことによって保持(永続化)し、まぁ使っている間だけ覚えていればいいや的な状態は、Application のメンバーとして持たせることにする。
Application のサブクラスを作成するのは、単純に、android.app.Application を継承したクラスを作成する。
ただし、それだけでは、そのアプリケーションクラスがメインのアプリケーションだと認識されていないので、AndroidManifest.xml に記述を追記してあげる必要がある。application の android:name に自作クラスを設定する。
<application android:name="CardroidApplication" android:icon="@drawable/icon" android:label="@string/app_name" :
もしくは、AndroidManifest.xml の設定画面、Application タブから、Name を設定する。
また一歩野望に近づいた。