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 を呼び出すと、

android_activity01 

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 実行中に。ふんふん。リファレンス通り。

 activity01

そして、画面を横にしてみる。

android_activity02

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()

activity02

おおっと、画面を横にした程度なら、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 を設定する。

android_application

また一歩野望に近づいた。