Activity异常销毁状态下的数据保存与恢复
在一些极端情况下我们的Activity会被销毁然后重建:例如内存不足时,我们界面被回收,APP在回到前台时系统根据栈信息为APP重新创建Activity,或者在屏幕旋转时Activity会重新创建。在重建的时候系统会默认为我们恢复一些状态,如果不了解没有处理就可能会出现一些预料不到的错误,数据的恢复和存储包括两方面:Activity和View
一、Activity的数据保存和恢复
在界面异常销毁之前会先调用onSaveInstanceState
保存数据,这个方法一般是在onPause
之后调用,我们可以在这个地方保存当前界面的数据和界面状态。当系统为我们重新创建Activity的时候,系统会调用onCreate
和onRestoreInstanceState
讲保存的数据返回给我们,我们从中取出保存的数据来为用户恢复到崩溃前的界面,这里有一点要注意的是,onCreate
每个创建都会被调用,而onRestoreInstanceState
只有在onSaveInstanceState
调用之后的创建才会被调用,开发者可以根据需求自己权衡使用哪个方法来恢复数据
二、View的数据保存和恢复
对于View
的重建比较特殊一点,因为系统已经为我们实现好了一部分工作,所以这个地方分成2部分:
1、系统默认控件
Android默认控件都已经为我们实现了数据保存和恢复机制,但是大部分都没有开启这个机制,需要我们自己去控制,例如TextView
,首先我们来看保存代码:
@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;
}
}
//判断并保存当前的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;
}
//保存是否有焦点
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
//保存错误信息
ss.error = getError();
//保存编辑状态
if (mEditor != null) {
ss.editorState = mEditor.saveInstanceState();
}
return ss;
}
return superState;
}
从上述代码和注释中可以看到当freezesText
为True
时就会保存Text,所以可以这么设置
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:freezesText="true" />
2、自定义控件的数据保存和恢复
因为自定义控件的数据不确定性,导致保存和恢复的逻辑只能我们自己来实现,但是也不会脱离View
的范围。下面以例子来说明实现方法:
1、保存数据的Model,这个要继承基础类
private static class PercentSaveState extends BaseSavedState {
private int mReachColor; // 达到的百分比颜色
private int mUnReachColor; // 没有达到的百分比颜色
private int mPercentTextColor;// 百分比数据的颜色
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];
}
};
}
这个主要是在保存数据的时候,要对数据进行序列化准备的
2、保存数据
@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、恢复数据
@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();
}
实现方法比较简单,但是有一点需要注意,在onRestoreInstanceState
中一定要调用super.onRestoreInstanceState
否则会报错,其原因就在我们继承的BaseSavedState
中。在BaseSavedState
中,系统还为我们处理了一个变量mStartActivityRequestWhoSaved
的存取,这个变量是用来接收startActivityForResult
处理结果的标示符,因此不能再重建的时候进行改变。