Last active
September 14, 2015 08:23
-
-
Save anolivetree/dd9963f137ebc721ae0f to your computer and use it in GitHub Desktop.
Activityの再生成にまつわる処理の煩雑さを解消するクラス
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.retain; | |
import android.annotation.TargetApi; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.os.Parcelable; | |
import java.io.Serializable; | |
import java.util.HashMap; | |
import java.util.concurrent.atomic.AtomicLong; | |
/** | |
* activity/fragmentのrecreationの時に、processが生きている限りはbundleよりも多くの情報を記録しておいてくれるクラス | |
* processが死んだ場合には、bundleの情報までしか復帰されない。 | |
* | |
* 使い方 | |
* 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); | |
* } | |
* | |
* @param <T> | |
*/ | |
public class Retain<T> { | |
static long sPrevId; | |
static final HashMap<Long, Object> sRetainData = new HashMap<>(); | |
static final String RETAIN_ID_KEY = "retain_id_key"; | |
static final String RETAIN_ID_VALUE = "retain_value_key"; | |
public long mId; | |
private final boolean mIsSerializable; | |
private Retain(boolean isSerializable) { | |
this.mId = getNewId(); | |
this.mIsSerializable = isSerializable; | |
} | |
static synchronized private long getNewId() { | |
long id = SystemClock.elapsedRealtime(); | |
if (sPrevId == id) { | |
id++; | |
} | |
sPrevId = id; | |
return id; | |
} | |
static public<T extends Serializable> Retain<T> forSerializable() { | |
return new Retain<T>(true); | |
} | |
static public<T extends Parcelable> Retain<T> forParcelable() { | |
return new Retain<T>(false); | |
} | |
public boolean wasProcessKilled() { | |
return mWasProcessKilled; | |
} | |
public boolean wasRecreated() { | |
return mWasRecreated; | |
} | |
public boolean mWasProcessKilled = false; | |
public boolean mWasRecreated = false; | |
// 初回起動の時: | |
// nullを返す。 | |
// wasRecreatedはfalse, wasProcessKilledはfalse | |
// 再生成でprocessが生きていた時: | |
// onSaveInstanceState()で渡されたTの参照を返す。 | |
// wasRecreatedはtrue。wasProcessKilledはfalse。 | |
// 再生成でprocessが死んでいた時: | |
// onSaveInstanceState()で渡されたTを、bundleから再生成したものを返す。 | |
// wasRecreatedはtrue。wasProcessKilledはtrue。 | |
public T onCreate(Bundle savedInstanceState) { | |
if (savedInstanceState == null) { | |
mWasRecreated = false; | |
mWasProcessKilled = false; | |
return null; | |
} else { | |
mWasRecreated = true; | |
long id = savedInstanceState.getLong(RETAIN_ID_KEY, -1); | |
if (id != -1) { | |
mId = id; | |
// staticなところからretainデータを取得 | |
// 取れたら、それを返す | |
try { | |
T data = (T) sRetainData.get(id); | |
if (data != null) { | |
return data; | |
} | |
} catch (ClassCastException e) { | |
// idは一意なので、違う型のオブジェクトが取れることは無いはず | |
throw new RuntimeException(e); | |
} | |
// idがbundleにあるのにstaticな領域にデータがないということは、processがkillされた場合 | |
// bundleから情報を復帰させる。 | |
mWasProcessKilled = true; | |
Object val = savedInstanceState.get(RETAIN_ID_VALUE); | |
if (val == null) { | |
// idとdataはonSaveInstanceで一緒に保存しているので、idがあるのにdataが無いことは無いはず | |
throw new RuntimeException("cannot find data in bundle"); | |
} else if (mIsSerializable && val instanceof Serializable) { | |
// val.getClass() == T.class みたいなことをしたいのだが… | |
return (T)val; | |
} else if (!mIsSerializable && val instanceof Parcelable) { | |
return (T)val; | |
} else { | |
// 誰かが同じキーを使って、違うデータを入れた? | |
throw new RuntimeException("cannot find data in bundle"); | |
} | |
} else { | |
// saveInstanceStateを呼んでいないのか? | |
throw new RuntimeException("you haven't called saveInstanceState?"); | |
} | |
} | |
} | |
// bundleとstatic領域にデータを保存する。 | |
// | |
// ここで渡したdataは、 | |
// - processが生きている限り、onSaveInstanceState()以後に行った変更でも、onCreate()で取得できる。 | |
// - processが死んだ場合には、onSaveInstanceState()でbundleに保存した時点の値が、onCreate()で取得できる。 | |
public void onSaveInstanceState(Bundle bundle, T data) { | |
bundle.putLong(RETAIN_ID_KEY, mId); | |
if (mIsSerializable) { | |
bundle.putSerializable(RETAIN_ID_VALUE, (Serializable) data); | |
} else { | |
bundle.putParcelable(RETAIN_ID_VALUE, (Parcelable) data); | |
} | |
sRetainData.put(mId, data); | |
} | |
public void onDestroy(Activity activity) { | |
if (activity != null && activity.isFinishing()) { | |
sRetainData.remove(mId); | |
} | |
} | |
@TargetApi(11) | |
public void onDestroy(android.app.Fragment f) { | |
if (f != null && f.isRemoving()) { | |
sRetainData.remove(mId); | |
} | |
} | |
public void onDestroy(android.support.v4.app.Fragment f) { | |
if (f != null && (f.isRemoving() || f.getParentFragment() != null && f.getParentFragment().isRemoving())) { | |
sRetainData.remove(mId); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment