Android 组件提供的状态保存(saveInstanceState)与恢复(restoreInstanceState)

Android的组件Activity中,有这样一对方法: onSaveInstanceeStateonRestoreInstanceState

这两对方法,可以让我在Activiy异常销毁时,保存状态;以及在Activity重建时,恢复状态。

比如:当我们在输入框中输入了内容,此时因为种种原因,将App退至了后台。这个处于后台的App很有可能因为内存不足、其他配置,被系统杀死。

当我们恢复这个页的时候,希望它能够保存住我们原来输入的内容。

除了,我们自己手动保存,也可以利用系统的onSaveInstanceStateonRestoreInstanceState

那么,在Android已有的系统中,是如何做的呢?

我们查阅EditText,发现它的父类TextView做了保存状态与恢复状态的处理,但是根据条件(freezesText || hasSelection)做了保存与恢复,如果只用TextView用于展示,并不会触发保存与恢复。

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;  
		}  
	}  
	// 满足此条件时,才进行保存数据
	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;  
}

// 恢复状态
@Override  
public void onRestoreInstanceState(Parcelable state) {  
	if (!(state instanceof SavedState)) {  
		super.onRestoreInstanceState(state);  
		return;  
	}  
  
	SavedState ss = (SavedState) state;  
	super.onRestoreInstanceState(ss.getSuperState());  
  
	// XXX restore buffer type too, as well as lots of other stuff  
	if (ss.text != null) {  
		setText(ss.text);  
	}  
  
	if (ss.selStart >= 0 && ss.selEnd >= 0) {  
		if (mSpannable != null) {  
			int len = mText.length();  
  
			if (ss.selStart > len || ss.selEnd > len) {  
				String restored = "";  
  
				if (ss.text != null) {  
					restored = "(restored) ";  
				}  
  
				Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd  
				+ " out of range for " + restored + "text " + mText);  
			} else {  
				Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);  
				if (ss.frozenWithFocus) {  
					createEditorIfNeeded();  
					mEditor.mFrozenWithFocus = true;  
				}  
			}  
		}  
	}  
  
	if (ss.error != null) {  
		final CharSequence error = ss.error;  
		// Display the error later, after the first layout pass  
		post(new Runnable() {  
				public void run() {  
					if (mEditor == null || !mEditor.mErrorWasChanged) {  
						setError(error);  
					}  
				}  
			});  
	}  
  
	if (ss.editorState != null) {  
		createEditorIfNeeded();  
		mEditor.restoreInstanceState(ss.editorState);  
	}  
}

onSaveInstanceState&onRestoreInstanceState的执行时机

这两个函数在什么情况下使用?比如开发者模式中开启了不保留活动、屏幕方向发生改变等原因,导致Activity(视图)被销毁或重建时,会执行。

被销毁时,执行onSaveInstanceState
重建时,执行onRestoreInstanceState

当然,这两个函数的执行也是有一些条件的,比如,View必须指定了Id,Id在整个视图(PhoneWindow)内必须唯一,如果不唯一则会在恢复状态时报错(保存时不会报错)。

在View的默认实现中,如果发现id一样,则会在恢复状态时报错。

@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.");  
}
// …… 省略剩余代码
}

我们在自定义View和使用第三方控件等情况下,需要合理处理这两个函数,否则会导致崩溃。

这里附一张InstanceState的执行时机图:

在这里插入图片描述

save的状态保存在哪里?restore的数据怎么取?

在View中,执行了onSaveInstanceState()后,View会将获取到的结果,保存在一个SparseArray中,这个SparseArray是从最根部的PhoneWindow中传递进来的,整个PhoneWindow中只有一份。

view中执行,container.put(mID, state);就会把自己要保存的数据放置到SparseArray中。

如果视图中存在id相同的View,那么后面保存的替换掉之前保存的。

在恢复数据时,也是从SparseArray中以当前View的Id为可以,获取保存的数据。获取到就是上一次保存时最后存储的数据。

Parcelable state = container.get(mID);  
if (state != null) {  
	// ……
	onRestoreInstanceState(state);
	// ……
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值