概述
前面的文章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。