在一些極端情況下我們的Activity會(huì)被銷毀然后重建:例如內(nèi)存不足時(shí),我們界面被回收,APP在回到前臺(tái)時(shí)系統(tǒng)根據(jù)棧信息為APP重新創(chuàng)建Activity,或者在屏幕旋轉(zhuǎn)時(shí)Activity會(huì)重新創(chuàng)建。在重建的時(shí)候系統(tǒng)會(huì)默認(rèn)為我們恢復(fù)一些狀態(tài),如果不了解沒(méi)有處理就可能會(huì)出現(xiàn)一些預(yù)料不到的錯(cuò)誤,數(shù)據(jù)的恢復(fù)和存儲(chǔ)包括兩方面:Activity和View
一、Activity的數(shù)據(jù)保存和恢復(fù)
在界面異常銷毀之前會(huì)先調(diào)用onSaveInstanceState保存數(shù)據(jù),這個(gè)方法一般是在onPause之后調(diào)用,我們可以在這個(gè)地方保存當(dāng)前界面的數(shù)據(jù)和界面狀態(tài)。當(dāng)系統(tǒng)為我們重新創(chuàng)建Activity的時(shí)候,系統(tǒng)會(huì)調(diào)用onCreate和onRestoreInstanceState講保存的數(shù)據(jù)返回給我們,我們從中取出保存的數(shù)據(jù)來(lái)為用戶恢復(fù)到崩潰前的界面,這里有一點(diǎn)要注意的是,onCreate每個(gè)創(chuàng)建都會(huì)被調(diào)用,而onRestoreInstanceState只有在onSaveInstanceState調(diào)用之后的創(chuàng)建才會(huì)被調(diào)用,開(kāi)發(fā)者可以根據(jù)需求自己權(quán)衡使用哪個(gè)方法來(lái)恢復(fù)數(shù)據(jù)
二、View的數(shù)據(jù)保存和恢復(fù)
對(duì)于View的重建比較特殊一點(diǎn),因?yàn)橄到y(tǒng)已經(jīng)為我們實(shí)現(xiàn)好了一部分工作,所以這個(gè)地方分成2部分:
1、系統(tǒng)默認(rèn)控件
Android默認(rèn)控件都已經(jīng)為我們實(shí)現(xiàn)了數(shù)據(jù)保存和恢復(fù)機(jī)制,但是大部分都沒(méi)有開(kāi)啟這個(gè)機(jī)制,需要我們自己去控制,例如TextView,首先我們來(lái)看保存代碼:
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
final boolean freezesText = getFreezesText();
boolean hasSelection = false;
int start = -1;
int end = -1;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
hasSelection = true;
}
}
//判斷并保存當(dāng)前的Text
if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
if (freezesText) {
if (mText instanceof Spanned) {
final Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
ss.text = mText.toString();
}
}
if (hasSelection) {
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
}
//保存是否有焦點(diǎn)
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
//保存錯(cuò)誤信息
ss.error = getError();
//保存編輯狀態(tài)
if (mEditor != null) {
ss.editorState = mEditor.saveInstanceState();
}
return ss;
}
return superState;
}
從上述代碼和注釋中可以看到當(dāng)freezesText為True時(shí)就會(huì)保存Text,所以可以這么設(shè)置
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:freezesText="true" />
2、自定義控件的數(shù)據(jù)保存和恢復(fù)
因?yàn)樽远x控件的數(shù)據(jù)不確定性,導(dǎo)致保存和恢復(fù)的邏輯只能我們自己來(lái)實(shí)現(xiàn),但是也不會(huì)脫離View的范圍。下面以例子來(lái)說(shuō)明實(shí)現(xiàn)方法:
1、保存數(shù)據(jù)的Model,這個(gè)要繼承基礎(chǔ)類
private static class PercentSaveState extends BaseSavedState {
private int mReachColor; // 達(dá)到的百分比顏色
private int mUnReachColor; // 沒(méi)有達(dá)到的百分比顏色
private int mPercentTextColor;// 百分比數(shù)據(jù)的顏色
private float mCircleWidth;// 圓形框的寬度
private float mPercent;// 百分比
PercentSaveState(Parcel source) {
super(source);
mReachColor = source.readInt();
mUnReachColor = source.readInt();
mPercentTextColor = source.readInt();
mCircleWidth = source.readFloat();
mPercent = source.readFloat();
}
PercentSaveState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mReachColor);
out.writeInt(mUnReachColor);
out.writeInt(mPercentTextColor);
out.writeFloat(mCircleWidth);
out.writeFloat(mPercent);
}
public static final Parcelable.Creator<PercentSaveState> CREATOR = new Parcelable.Creator<PercentSaveState>() {
@Override
public PercentSaveState createFromParcel(Parcel source) {
return new PercentSaveState(source);
}
@Override
public PercentSaveState[] newArray(int size) {
return new PercentSaveState[size];
}
};
}
這個(gè)主要是在保存數(shù)據(jù)的時(shí)候,要對(duì)數(shù)據(jù)進(jìn)行序列化準(zhǔn)備的
2、保存數(shù)據(jù)
@Override
protected Parcelable onSaveInstanceState() {
Log.i(TAG, "onSaveInstanceState: ");
Parcelable superState = super.onSaveInstanceState();
PercentSaveState saveState = new PercentSaveState(superState);
saveState.mPercent = mPercent;
saveState.mCircleWidth = mCircleWidth;
saveState.mPercentTextColor = mPercentTextColor;
saveState.mUnReachColor = mUnReachColor;
saveState.mReachColor = mReachColor;
return saveState;
}
3、恢復(fù)數(shù)據(jù)
@Override
protected void onRestoreInstanceState(Parcelable state) {
Log.i(TAG, "onRestoreInstanceState: ");
if (!(state instanceof PercentSaveState)) {
super.onRestoreInstanceState(state);
}
PercentSaveState saveState = (PercentSaveState) state;
super.onRestoreInstanceState(saveState.getSuperState());
mCircleWidth = saveState.mCircleWidth;
mPercent = saveState.mPercent;
mReachColor = saveState.mReachColor;
mUnReachColor = saveState.mUnReachColor;
mPercentTextColor = saveState.mPercentTextColor;
invalidate();
}
實(shí)現(xiàn)方法比較簡(jiǎn)單,但是有一點(diǎn)需要注意,在onRestoreInstanceState中一定要調(diào)用super.onRestoreInstanceState否則會(huì)報(bào)錯(cuò),其原因就在我們繼承的BaseSavedState中。在BaseSavedState中,系統(tǒng)還為我們處理了一個(gè)變量mStartActivityRequestWhoSaved的存取,這個(gè)變量是用來(lái)接收startActivityForResult處理結(jié)果的標(biāo)示符,因此不能再重建的時(shí)候進(jìn)行改變。