Activity 的狀態(tài)保存與還原
Activity 的狀態(tài)保存
一般來(lái)說(shuō)再来,當(dāng) Activity 暫臀钫叮或停止(onPause() 或 onStop() Activity 不再處于前臺(tái))時(shí),Activity 的狀態(tài)會(huì)得到保留它掂。因?yàn)楫?dāng)Activity 暫停或停止時(shí),對(duì)象仍保留在內(nèi)存中 — 有關(guān)其成員和當(dāng)前狀態(tài)的所有信息仍處于活動(dòng)狀態(tài)焊傅。 因此,用戶在 Activity 內(nèi)所做的任何更改都會(huì)得到保留狈涮,這樣一來(lái)狐胎,當(dāng) Activity 返回前臺(tái)(當(dāng)它“繼續(xù)”)時(shí),這些更改仍然存在歌馍。但是握巢,當(dāng)系統(tǒng)內(nèi)存不足,為了恢復(fù)內(nèi)存而銷毀某個(gè) Activity 時(shí)松却,Activity 的對(duì)象也會(huì)被銷毀暴浦。因此系統(tǒng)在繼續(xù) Activity 時(shí)根本無(wú)法讓其狀態(tài)保持完好,而是必須在用戶返回 Activity 時(shí)重建 Activity 對(duì)象(重新 onCreate() )晓锻。但用戶并不知道系統(tǒng)銷毀 Activity 后又對(duì)其進(jìn)行了重建歌焦,因此他們很可能認(rèn)為 Activity 狀態(tài)毫無(wú)變化,這時(shí)可能會(huì)把用戶希望保存的信息也一起銷毀掉砚哆,導(dǎo)致不好的用戶體驗(yàn)独撇。為了處理這種情況,Android 為我們提供了一個(gè) onSaveInstanceState() 的方法躁锁,我們可以回調(diào)這個(gè)方法來(lái)保存一些用戶希望保存的信息纷铣。在 Activity 變得易于銷毀,所謂的易于銷毀灿里,在Android 的官方文檔中提到关炼,onSaveInstanceState() 這個(gè)方法是在 onPause() 之前或者之后但肯定是在 onStop() 之前執(zhí)行的,就是還未銷毀之前匣吊,系統(tǒng)會(huì)先回調(diào)onSaveInstanceState() 儒拂。
使用情景
(1)當(dāng)用戶按下HOME鍵時(shí)寸潦。
(2)選擇運(yùn)行其他的程序時(shí)。
(3)按下電源按鍵(關(guān)閉屏幕顯示)時(shí)社痛。
(4)從 Activity A 中啟動(dòng)另一個(gè) Activity 時(shí)见转。
(5)屏幕方向切換時(shí),例如從豎屏切換到橫屏?xí)r蒜哀。
來(lái)看看 onSaveInstanceState() 的使用
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* 可以在次使用 Bundle 的一系列方法保存信息
* 比如:
* outState.putInt();
*/
}
從上面的代碼可以發(fā)現(xiàn)這里傳入的是一個(gè) Bundle 對(duì)象斩箫,可以在里面使用putInt()、putString()等一系列方法保存信息撵儿。
再來(lái)看下 onSaveInstanceState() 的源碼
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
由上面代碼可知乘客,outState 通過(guò) put 一個(gè) TAG 給mWindow.saveHierarchyState()。
繼續(xù)跟蹤淀歇,由于 Window 是抽象類易核,查看其子類PhoneWindow的saveHierarchyState()方法。
/** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);
// 代碼到此即可知道 Bundle 是怎么保存的
/**
* 此處省略一系列代碼
*/
return outState;
}
在上面代碼中:
- Bundle outState = new Bundle(); 初始化了一個(gè) Bundle 對(duì)象浪默。
- mContentParent 是一個(gè) ViewGroup 對(duì)象的實(shí)例牡直,當(dāng)它為空時(shí),就直接返回一個(gè)空的 Bundle纳决。
- SparseArray<Parcelable> states = new SparseArray<Parcelable>(); states最終存到outState中碰逸。(Bundle 對(duì)象實(shí)現(xiàn)了 Parcelable 接口)
- 繼續(xù)跟蹤 mContentParent.saveHierarchyState(states);
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
上面代碼是 View.java 里面的,看不出什么阔加,繼續(xù)跟蹤饵史,看看它的dispatchSaveInstanceState() 方法。
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);
}
}
}
從上面的代碼:
首先要搞清楚里面的一些常量是什么
1)NO_ID
/**
* Used to mark a View that has no ID.
*/
public static final int NO_ID = -1;
由注釋可知它是用來(lái)標(biāo)志一個(gè)沒(méi)有 id 的 View 掸哑。
2)mID
@IdRes
@ViewDebug.ExportedProperty(resolveId = true)
int mID = NO_ID;
public void setId(@IdRes int id) {
mID = id;
if (mID == View.NO_ID && mLabelForId != View.NO_ID) {
mID = generateViewId();
}
}
每個(gè) View 都有它的 ID 约急,要么在 xml 中指定,要么在代碼中setID()苗分,要么是默認(rèn) ID。告誡大家盡量別用同樣的 ID 命名 View 牵辣,否則會(huì)有意想不到的 Bug摔癣。
3)PFLAG_SAVE_STATE_CALLED
private static final int PFLAG_SAVE_STATE_CALLED = 0x00020000;
這個(gè)應(yīng)該是是否在子類實(shí)現(xiàn) onSaveInstanceState() 的標(biāo)志,通過(guò)下面的代碼打印的異澄诚颍可以推斷
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
繼續(xù)看 Parcelable state = onSaveInstanceState();
@CallSuper
protected Parcelable onSaveInstanceState() {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (mStartActivityRequestWho != null) {
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
return state;
}
return BaseSavedState.EMPTY_STATE;
}
由上面代碼可知择浊,默認(rèn)設(shè)置的標(biāo)志位為空,若不為空逾条,則返回相應(yīng)的state
最后由 container 將相應(yīng)的 View 的 ID 和 state 進(jìn)行保存琢岩。代碼如下
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);
}
至此,我們可以知道關(guān)于 onSaveInstanceState()师脂,保存了 View 有用的數(shù)據(jù)担孔,包括 ID 和 View 的各種狀態(tài)到一個(gè) Parcelable 對(duì)象并返回江锨。在Activity 的 onSaveInstanceState(Bundle outState) 中通過(guò) Window 的
saveHierarchyState() 方法,最終調(diào)用 View 的 onSaveInstanceState ()糕篇,返回Parcelable對(duì)象啄育,接著用 Bundle 的 putParcelable 方法保存在 Bundle 的實(shí)例 outState 中。key 值為WINDOW_HIERARCHY_TAG拌消。
Activity 的狀態(tài)還原
如果系統(tǒng)終止您的應(yīng)用進(jìn)程之后挑豌,用戶返回您的 Activity,則系統(tǒng)會(huì)重建該 Activity墩崩,并將 Bundle 同時(shí)傳給 onCreate() 和 onRestoreInstanceState()氓英。可以使用上述任一方法從 Bundle 提取您保存的狀態(tài)并恢復(fù)該 Activity 狀態(tài)鹦筹。如果沒(méi)有狀態(tài)信息需要恢復(fù)铝阐,則傳遞給您的 Bundle是為null(如果是首次創(chuàng)建該 Activity,就會(huì)出現(xiàn)這種情況)盛龄。
來(lái)看它的使用吧
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
/**
* 可以在次使用 Bundle 的一系列方法來(lái)獲取信息
* 比如:
* savedInstanceState.getInt();
*/
}
繼續(xù)看
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
在 onSaveInstanceState() 中饰迹,Bundle 的 key 值為 WINDOW_HIERARCHY_TAG,在這里自然也使用它來(lái)獲取數(shù)據(jù)余舶。接著啊鸭,如果數(shù)據(jù)不為空,則使用 restoreHierarchyState() 匿值。繼續(xù)跟蹤
/** {@inheritDoc} */
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
mContentParent.restoreHierarchyState(savedStates);
}
// 省略部分代碼
}
}
}
由上面源碼中可知
先通過(guò) VIEWS_TAG 獲取 View 的信息赠制。再通過(guò) View 的 restoreHierarchyState() 方法還原。繼續(xù)跟蹤
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
繼續(xù)
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state);
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}
哎挟憔,有種似曾相識(shí)的感覺(jué)钟些,從上面可知,先是通過(guò) mID 獲取返回一個(gè) Parcelable 對(duì)象實(shí)例 state绊谭,接著再使用 View 的 onRestoreInstanceState() 方法政恍,繼續(xù)看
@CallSuper
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "
+ "received " + state.getClass().toString() + " instead. This usually happens "
+ "when two views of different type have the same id in the same hierarchy. "
+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
+ "other views do not use the same id.");
}
if (state != null && state instanceof BaseSavedState) {
mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
}
}
最終是在 View 的 onRestoreInstanceState() 方法中找到是在哪里保存的狀態(tài)。至此达传,分析結(jié)束篙耗。
最后看一個(gè)整體圖