Activity 和Fragment 的异常销毁、状态保存和恢复机制

  • 异常销毁
  • 状态类型
    • Activity /Fragment 状态
    • View状态
  • View状态的保存流程
    • View tree深度遍历保存View状态的流程
    • Activity 的View状态保存流程
    • Fragment 的View状态保存流程
  • View状态的恢复流程
    • View tree深度遍历恢复View状态的流程
    • Activity的View状态的恢复流程
    • Fragment的View状态的恢复流程

系列
Fragment (一):Fragment使用详解
Fragment (二):Activity 和Fragment 的异常销毁、状态保存和恢复机制
Fragment (三):源码解析Fragment 返回栈原理

异常销毁


不了解Fragment 使用、生命周期、常用API等的可用先简单看下:Fragment使用详解

  • 概念:异常销毁,即不是由于用户主动退出Activity导致其被销毁(比如按BACK键后Activity被主动销毁),而是由于其他非正常情况导致的销毁。
    注:Activity的异常销毁会导致Fragment的异常销毁。

可能导致Activity的异常销毁的几种情况:

  • 按HOME键返回桌面时
  • 按菜单键回到系统后台,并选择了其他应用时
  • 按电源键时
  • 屏幕方向切换时

Android系统中,当可能导致Activity的异常销毁时,会触发Activity和它的所有active fragment 的onSaveInstanceState()保存开发者需要保存的Activity /Fragment状态,同时还会自动保存它们各自的View 状态。若Activity被异常销毁,再次回到该Activity时会重建该Activity和它被销毁前的所有active fragment,并恢复被保存的状态。
**注:**fragment 的hide 和show 不会被保存,所有重建的fragment 的状态都是show,除非在fragment的oncreate()中设置setRetianInstance(true),因为该fragment不会随宿主Activity的销毁而销毁,但该fragment 不能加入返回栈,具体参考:Fragment使用详解中没有视图的Fragment的用处。

“前台”fragment:调用了add()但既没有被销毁也尚未被移到返回栈中的fragment 。

FragmentManagerImpl 中有两个重要的Field:

  1. mAdded:fragment列表,保存所有前台fragment 。
  2. mActive:fragment列表,保存所有前台fragment 和返回栈中的fragment ,实际上就是active fragment。

状态类型


状态分两种:

  1. Activity /Fragment状态:开发者手动保存Activity /Fragment 持有的变量。
  2. View状态:系统自动保存设置了id的View的状态(TextView还需要设置android:freezeText=”true”)。

Activity /Fragment 状态

触发Activity /Fragment 状态保存和恢复只有一种情形:异常销毁

  • Activity /Fragment 状态的保存
    当可能发生异常销毁时会自动调用Activity和所有active fragment 的onSaveInstanceState()。开发者可重写Activity /Fragment 的onSaveInstanceState(),保存Activity /Fragment状态到savedInstanceState() 中(注:不要忘了调用父类方法)。

    Activity的onSaveInstanceState()在onStop()之后,onDestroy()之前或之后触发。

  • Activity 状态的恢复
    可以在onCreate(Bundle savedInstanceState)、onRestoreInstanceState(Bundle savedInstanceState)这两个方法中的savedInstanceState 获取保存的Activity 状态。

  • Fragment 状态的恢复
    可以在onCreate(Bundle savedInstanceState)、onActivityCreated(Bundle savedInstanceState)这两个方法中的savedInstanceState 获取保存的Fragment 状态。

Activity /Fragment 状态保存适用于保存占用存储空间较小的数据,若有数据占用存储空间较大,比如图片,则需要使用没有UI 的Fragment来保存,具体参考:fragment 状态保存和恢复。

View状态

实现View状态的自动保存,需要为每个View 设置id。TextView 还需要设置android:freezeText=”true”。

触发Activity 的View 状态保存和恢复只有一种情形:异常销毁

触发Fragment 的View 状态保存和恢复的两种情形
1、异常销毁。
2、事务进出返回栈。

  • View状态的保存
    无论是对Activity还是Fragment中View状态的保存,本质上都是通过调用Activity /Fragment 中顶级View的saveHierarchyState() 从而实现对Activity /Fragment 中View tree的深度遍历并调用每个View 的onSaveInstanceState() 保存View状态
    除了Fragment 的View状态是保存在Fragment 的一个Field 中外,Activity /Fragment 状态和View状态最终都保存在Activity的一个Field 中。(具体看后面源码分析)

    1、异常销毁:Activity的onSaveInstanceState() 中会调用:

    1. Activity的顶级View的saveHierarchyState()。
    2. Activity中所有active fragment 的onSaveInstanceState() 和它们的顶级View 的saveHierarchyState()。
      注意:Fragment 中顶级View 的saveHierarchyState()不是在它的onSaveInstanceState() (是空实现)中被调用的,这与Activity不同。

    2、事务进栈:事务通过addToBackStack() 添加到返回栈中会导致所有从前台进入返回栈的Fragment 的顶级View的saveHierarchyState() 被调用。注意:不会触发Activity 和Fragment 的onSaveInstanceState()。

  • View状态的恢复
    无论是对Activity还是Fragment中View状态的保存,本质上都是通过调用Activity /Fragment 中顶级View的restoreHierarchyState() 从而实现对Activity /Fragment 中View tree的深度遍历并调用每个View的onRestoreInstanceState() 恢复View状态。

    1、异常销毁:会触发Activity 和所有active fragment 顶级View的restoreHierarchyState() 。

    2、事务出栈:事务出栈会触发该事务涉及到的所有从返回栈出栈转为前台的Fragment 的顶级View的restoreHierarchyState()。

从上面两点可知,自定义View要实现自身状态的保存和恢复,需要重写onSaveInstanceState() 和onRestoreInstanceState()。参考:( )
下面主要通过源码介绍系统自动对View状态的保存和恢复,可以不用看。

View状态的保存流程


View tree深度遍历保存View状态的流程

Activity /Fragment中View tree的深度遍历并保存View状态的过程,即View的saveHierarchyState(SparseArray container)方法的流程:

  1. ViewGroup:saveHierarchyState() (该方法继承自View)
  2. ViewGroup:dispatchSaveInstanceState() 遍历子View
  3. 继续分发……
  4. View:dispatchSaveInstanceState()
  5. View:onSaveInstanceState()

View:

/**
 * Store this view hierarchy's frozen state into the given container.
 */
public void saveHierarchyState(SparseArray container) {
    dispatchSaveInstanceState(container);
}

protected void dispatchSaveInstanceState(SparseArray container) {
    if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
        mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
        Parcelable state = onSaveInstanceState();//调用View的onSaveInstanceState()方法保存自身状态。
        if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
            throw new IllegalStateException(
                    "Derived class did not call super.onSaveInstanceState()");
        }
        if (state != null) {
            container.put(mID, state);//这是为何View必须设置id才能保存状态的原因。
        }
    }
}

ViewGroup:
注意:ViewGroup并未重写View的saveHierarchyState()。

@Override
protected void dispatchSaveInstanceState(SparseArray container) {
    super.dispatchSaveInstanceState(container);
    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        View c = children[i];
        if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
            c.dispatchSaveInstanceState(container);//遍历子View
        }
    }
}

Activity 的View状态保存流程

只有异常销毁才会触发Activity 的View状态保存。
Activity异常销毁时是如何触发View tree 的深度遍历并保存状态的?流程如下:

  1. Activity:onSaveInstanceState()
  2. PhoneWindow:saveHierarchyState()
  3. ViewGroup:saveHierarchyState() View tree的深度遍历

Activity:

protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//遍历Activity的View tree并自动保存View状态
    Parcelable p = mFragments.saveAllState();//FragmentController的方法
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

PhoneWindow:

/** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
    Bundle outState = new Bundle();
    if (mContentParent == null) {
        return outState;
    }

    SparseArray states = new SparseArray();
    mContentParent.saveHierarchyState(states);//View tree根结点
    outState.putSparseParcelableArray(VIEWS_TAG, states);

    ......

    return outState;
}

// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;

Fragment 的View状态保存流程

1、异常销毁:
Activity异常销毁时是如何触发active fragment 的View tree 的深度遍历并保存状态的?流程如下:

  1. Activity:onSaveInstanceState()
  2. FragmentController:saveAllState()
  3. FragmentManagerImpl:saveAllState()
  4. FragmentManagerImpl:saveFragmentBasicState()
  5. FragmentManagerImpl:saveFragmentViewState()
  6. ViewGroup:saveHierarchyState() View tree的深度遍历

Activity:

protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//遍历Activity的View tree并自动保存View状态
    Parcelable p = mFragments.saveAllState();//FragmentController的方法
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

FragmentController:

/**
 * Saves the state for all Fragments.
 */
public Parcelable saveAllState() {
    return mHost.mFragmentManager.saveAllState();//mFragmentManager是FragmentManagerImpl的对象。
}

FragmentManagerImpl:

Parcelable saveAllState() {
    ......
    // First collect all active fragments.
    int N = mActive.size();
    FragmentState[] active = new FragmentState[N];
    boolean haveFragments = false;
    for (int i=0; i//遍历所有active fragment
        Fragment f = mActive.get(i);//取出active fragment
        if (f != null) {
            ......

            if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                fs.mSavedFragmentState = saveFragmentBasicState(f);//
                ......
            } else {
                fs.mSavedFragmentState = f.mSavedFragmentState;
            }
        }
    }

    ......

    FragmentManagerState fms = new FragmentManagerState();
    fms.mActive = active;
    fms.mAdded = added;
    fms.mBackStack = backStack;
    return fms;
}


Bundle saveFragmentBasicState(Fragment f) {
    Bundle result = null;

    if (mStateBundle == null) {
        mStateBundle = new Bundle();
    }
    f.performSaveInstanceState(mStateBundle);//最终触发Fragment的onSaveInstanceState(Bundle outState)
    if (!mStateBundle.isEmpty()) {
        result = mStateBundle;
        mStateBundle = null;
    }

    if (f.mView != null) {
        saveFragmentViewState(f);//系统自动保存Fragment中的View状态
    }
    ......

    return result;
}


void saveFragmentViewState(Fragment f) {
    if (f.mInnerView == null) {
        return;
    }
    if (mStateArray == null) {
        mStateArray = new SparseArray();
    } else {
        mStateArray.clear();
    }
    f.mInnerView.saveHierarchyState(mStateArray);//mInnerView就是Fragment的根布局,即View tree的根结点。
    if (mStateArray.size() > 0) {
        f.mSavedViewState = mStateArray;
        mStateArray = null;
    }
}

// The real inner view that will save/restore state.
//实际上就是Fragment的布局,也就是Fragment的onCreateView()中返回的View,会在FragmentManager的moveToState()中被初始化。
View mInnerView;

2、事务进栈:
Fragment事务加入返回栈导致时是如何触发active fragment 的View tree 的深度遍历并保存状态的?流程如下:

  1. getFragmentManager().beginTransation():获得BackStackRecord
  2. BackStackRecord:addToBackStack()
  3. BackStackRecord:commit()
  4. BackStackRecord:commitInternal()
  5. FragmentManagerImpl:enqueueAction():BackStackRecord加入mPendingActions。
  6. FragmentManagerImpl:generateOpsForPendingActions():mPendingActions转换为mTmpRecords 和mTmpIsPop。
  7. FragmentManagerImpl:executeOpsTogether() :遍历mTmpRecords。1、将Op是replace 的替换为多个remove 和一个add。2、执行所有Op,更新mAdded 和mActive,最后一个Op时,对mAdded 和mActive 中所有Fragment 切换生命周期到预期的状态,同时,在此过程中对每一个从前台退回到返回栈的Fragment调用它的顶级View 的saveHierarchyState() 保存View状态。

详细介绍参考:源码解析Fragment 返回栈原理

View状态的恢复流程


View tree深度遍历恢复View状态的流程

Activity /Fragment中View tree的深度遍历并恢复View状态的过程,即View的restoreHierarchyState(SparseArray container)方法的流程:

  1. ViewGroup:restoreHierarchyState() (该方法继承自View)
  2. ViewGroup:dispatchRestoreInstanceState() 遍历子View
  3. 继续分发……
  4. View:dispatchRestoreInstanceState()
  5. View:onRestoreInstanceState()

Activity的View状态的恢复流程

流程:

  1. Activity:onRestoreInstanceState()
  2. PhoneWindow:restoreHierarchyState()
  3. ViewGroup:restoreHierarchyState()

Fragment的View状态的恢复流程

1、异常销毁
在Fragment 重建过程中调用自身顶级View的restoreHierarchyState() 。

2、事务出栈

  1. FragmentManagerImpl:popBackStack()
  2. FragmentManagerImpl:enqueueAction():BackStackRecord加入mPendingActions。
  3. FragmentManagerImpl:generateOpsForPendingActions():mPendingActions转换为mTmpRecords 和mTmpIsPop。
  4. FragmentManagerImpl:executeOpsTogether() :遍历mTmpRecords。1、将Op是replace 的替换为多个remove 和一个add。2、执行所有Op,更新mAdded 和mActive,最后一个Op时,对mAdded 和mActive 中所有Fragment 切换生命周期到预期状态,同时,在此过程中对每一个从返回栈出栈变为前台的Fragment调用它的顶级View的restoreHierarchyState() 恢复View状态。

详细介绍参考:源码解析Fragment 返回栈原理


你可能感兴趣的:(Activity 和Fragment 的异常销毁、状态保存和恢复机制)