Android状态保存与恢复流程 完全解析

前言

很久没写过文章了,最近一段时间忙着各种各样的事情,难得寒假有时间便把最近所学的整理及记录下来与大家分享。本篇文章是关于Android的状态保存与恢复的源码分析。
对于一个Activity或者View来说,状态的保存与恢复是必不可少的,最常见的一种情况是切换屏幕方向了,如果由竖屏切换为横屏,那么必定会经历Activity的摧毁与重建,那么它所对应的View视图也会被摧毁和重建,如果此时没有对View进行状态的保存的话,那么待View重建后,其之前的状态便不复存在。庆幸的是,对于系统自带的View(比如CheckBox等),Android已经帮我们实现了状态的自动保存与恢复,不必我们费心了。但是对于我们自己开发的自定义View,就需要我们去保存状态和恢复状态了,系统提供了两个API方便我们去实现保存和恢复,分别是onSaveInstanceStateonRestoreInstanceState这两个方法,我们只需要重写这两个方法,然后在里面写需要的逻辑即可。那么接下来就分析整个保存和恢复的流程是怎样的。

保存状态流程分析

从Activity的onSaveInstanceState说起

Activity有这样一个方法:onSaveInstanceState (Bundle outState),相信大家都不陌生,我们通过重写这个方法来保存我们需要的数据。先来看看官方文档对这个方法的注释:

Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method will be passed to both).
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state

可以知道,当一个Activity变得容易被kill的时候会调用这个方法,比如说当旋转屏幕的时候,当前Activity在onDestroy之前会调用onSaveInstanceState来保存状态,又或者说当前Activity被置于后台了,系统内存紧张的时候会有可能杀死这个Activity,那么此时Activity也会保存状态。
那么我们来看看这个方法内部做了些什么,Activity#onSaveInstanceState:

protected void onSaveInstanceState(Bundle outState) {
    //1、保存View树状态,key是WINDOW_HIERARCHY_TAG
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    //2、保存Fragment状态,key是FRAGMENTS_TAG
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

显然,数据是存放在一个Bundle里面的。首先,调用了mWindow的saveHierarchyState()方法,遍历Activity的View树来保存数据,接着调用mFragments.saveAllState()对与Activity所关联的Fragment保存数据。下面,我们先来看看怎样保存View树的数据的:

1、保存View树数据

从上面可以看出,调用了Window的saveHierarchyState方法,而Window是一个抽象类,它的实现类是PhoneWindow,那么我们来看PhoneWindow#saveHierarchyState:

@Override
public Bundle saveHierarchyState() {
    Bundle outState = new Bundle();
    if (mContentParent == null) {
        return outState;
    }
    SparseArray<Parcelable> states = new SparseArray<Parcelable>();
    mContentParent.saveHierarchyState(states);
    outState.putSparseParcelableArray(VIEWS_TAG, states);
    //省略...

    return outState;
}

这里先是实例化了一个SparseArray,它和HashMap类似,保存着键值对,接着调用了mContentParent的saveHierarchyState()方法,并把结果放进outState中并返回。这里的mContentParent是DecorView的子元素或者其自身,我之前的文章也有所提及,这里可以把mContentParent看做整个View树的顶层视图,由于mContentParent是一个ViewGroup,但是ViewGroup没有重写saveHierarchyState方法,那么这里调用的便是View#saveHierarchyState:

public void saveHierarchyState(SparseArray<Parcelable> container) {
     dispatchSaveInstanceState(container);
}

从名字可以猜测,接下来就是分发保存事件了,遍历所有子View,让所有的子View去保存数据,我们来看看ViewGroup#dispatchSaveInstanceState是否如我们所想的那样:

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    //调用View的dispatchSaveInstanceState方法,目的是保存ViewGroup自身的状态
    super.dispatchSaveInstanceState(container);
    final int count = mChildrenCount;
    final View[] children = mChildren;
    //遍历所有的子View,保存状态,所有状态都放在SparseArray内
    for (int i = 0; i < count; i++) {
        View c = children[i];
        if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
            c.dispatchSaveInstanceState(container);
        }
    }
}

从上面源码可以看出&#x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值