google-code-prettify

2015-06-10

Activityの再生成にまつわる処理を書くベストな方法

AndroidでActivityの再生成にまつわる問題をちゃんと解決しようとすると、非常に煩雑なコードになる。

その煩雑さを解消するクラスを作った。今の所、この方法がベストな方法だと思う。
Retain.java

以下、Activityの再生成に関する典型的な対処法とその問題点。

Activityの再生成のパターンは2つある


Activityは、以下の場合に再生成される。
  1. Configuration changeの時(画面の回転など)。プロセスは生きたまま。
  2. バックグラウンドにいる間にOSによってプロセスごと殺された後、アプリに戻ってきた時。
再生成後の新しいinstanceにデータを渡したいときにどう渡すかが問題となる。

方法1. onSaveInstanceStateでbundleに入れる

  • processが死んでいた場合でも、データが残っている。
  • Bundleに入る型しか保持できない(Integer, Boolean, String, Serializableなど)。
    例えば、通信処理スレッドを再生成後のinstanceに引き渡す、ということは直接はできない。
  • onSaveInstanceState()が呼ばれた後には、もうbundleに入れることができない。

方法2. staticな領域に覚えておく

  • processが死んでいない場合は、データは残っている。
  • processが死んでいた場合は、データは残っていない。
  • Activityが2つ存在するときに、間違って別のActivityにデータを渡さないようにしなくてはならない。

方法3. retain用のFragmentを使う

setRetainInstance(true)なFragmentを用意して、そのretain用Fragmentにデータを覚えさせておく方法。 http://developer.android.com/guide/topics/resources/runtime-changes.html
  • processが死んでいない場合は、データは残っている。
  • processが死んでいた場合は、データは残っていない。
  • Activityに紐付いたデータ(ThreadにおけるThread Local Storageのように)なので、別のActivityに間違ってデータを渡してしまうことはない。
  • Activityがいなくなると、自動でFragmentも消してくれる。
方法3の問題点
  • あくまでActivityに結びついた情報なので、Activityに含まれるFragmentごとに別々のretain用fragmentをもたせる、と言ったことは非常にしにくい。
  • retain用Fragmentを削除するタイミングが難しい。下手なタイミングでFragmentTransactionをすると、IllegalStateExceptionが出る。
  • FragmentActivity, Fragmentを使わなくてはならない。
  • コードが読みにくい

解決方法

  • processが死んだ場合でも、できるだけ情報を残したい、
  • でも、bundleに入る情報以外も保持したい
  • 3の方法は嫌だ
となると結局1と2のハイブリッドな方法で行くしか無い。 1,2の問題点を解消しつつ、1も2も統一的な方法で扱えるためのクラスを作った。 Retain.java

使い方

private Retain<somedata> retain; // SomeDataは、ParcelableまたはSerializableであること

@Override
protected void onCreate(Bundle savedInstanceState) {
    retain = Retain.forSerializable()
    someData = retain.onCreate(savedInstanceState);
    if (someData == null) {
        someData = new SomeData()
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    retain.onSaveInstanceState(outState, someData);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    retain.onDestroy(this);
}

これだけで、someDataの情報はできるだけ保持されるし、画面回転等にも対処できる。

0 件のコメント: