概述
前面的文章Android – Activity 之 onSaveInstanceState 深入理解  介绍了 Activity 状态保存机制,本文介绍一下 View 的状态保存机制。
View 和 Activity 类似,也有两个状态保存的方法:
- public Parcelable onSaveInstanceState()
 - public void onRestoreInstanceState(Parcelable state)
 
当 View 因意外销毁或者因旋转屏幕导致的销毁时,可以通过 onSaveInstanceState 方法保存我们需要保存的数据,当重新创建 View 时,可以通过 onRestoreInstanceState 方法来恢复我们的数据。
使用方法
在 View 类中,状态保存的相关方法有:
- dispatchSaveInstanceState
 - onSaveInstanceState
 - restoreHierarchyState
 - dispatchRestoreInstanceState
 - onRestoreInstanceState
 
我们只要重新实现 onSaveInstanceState 和 onRestoreInstanceState 方法就能实现状态保存的需求。
一些 View 的派生类比如 TextView 也是覆盖了这些方法来实现状态保存的,比如文本是否选择,以及选择的起始和结束位置等。在状态保存之前可以先看父类是否已经为我们完成了我们的需求,如果没有,再自己实现既可。
1  | public class CustomTextView extends EditText {  | 
源码分析
在前面的文章Android – Activity 之 onSaveInstanceState 深入理解 曾经提到过 Activity 的 onSaveInstanceState 方法会保存 View 的一些状态:
1  | protected void onSaveInstanceState(Bundle outState) {  | 
那么我们就接着 View 保存这部分接着分析。
mWindow 是 PhoneWindow 的实例,我们来看一下 PhoneWindow 的 saveHierarchyState 方法:
1  | Bundle outState = new Bundle();  | 
mContentParent 其实就是 id 为 com.android.internal.R.id.content 的 ViewGroup,在 ViewGroup 中没有重写 saveHierarchyState 方法,因此 mContentParent.saveHierarchyState(states) 调用的是 View 的 saveHierarchyState 方法。
View.saveHierarchyState
1  | public void saveHierarchyState(SparseArray<Parcelable> container) {  | 
从上面的 dispatchSaveInstanceState 可以看出,View 必须设置 id,否则是不会调用 onSaveInstanceState 来保存状态的。
和 android:id 的关系
结合上面的 dispatchSaveInstanceState 方法还有下面的 dispatchRestoreInstanceState 方法,可以得出结论,View 的 onSaveInstanceState 和 onRestoreInstanceState 方法的调用,都必须为这个 View 设置 id。
1  | protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {  | 
在上面方法中,状态的恢复是从 SparseArray 中根据 View 的 id 来去除状态的来进行保存并调用 onRestoreInstanceState 方法,这里就要注意,在当前 View 树中不要设置两个相同的 id。否则这里的状态可能会发生覆盖的现象。这种情况很容易出现在 ViewPager 这种多页面的布局中。
其实Android官方的说法是在整个view树中id不一定非要唯一,但你至少要
保证在你搜索的这部分view树中是唯一的(局部唯一)。因为很显然,如果同一个 layout 文件中有2个 id 都是 “android:id=”@+id/button”
的 Button,那你通过 findViewById 的时候只能找到前面的 button,后面的那个就没机会被找到了,所以 Android 的说法是合理的。
和 android:saveEnabled 的关系
前面的 View 的 dispatchSaveInstanceState 方法中,
1  | protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {  | 
(mViewFlags & SAVE_DISABLED_MASK) == 0满足条件才会调用 onSaveInstanceState 保存状态,SAVE_DISABLED_MASK 是通过 android:saveEnabled 来设置的,默认是 true。