概述

今天我们从源码的角度来分析一下Activity,Fragment状态保存和恢复的过程,以及如何在自定义View中保存和恢复状态。

onSaveInstanceState方法里保存Activity状态。


protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());//mWindow保存View树状态,将保存后的状态保存到Bundle中
Parcelable p = mFragments.saveAllState();//保存Fragment状态,获得保存的结果Parcelable
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);//把代表Fragment状态的Parcelable保存到Bundle
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}

我们知道mWindow是PhoneWindow的对象,

@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();//创建一个空的Bundle对象,用来保存数据
if (mContentParent == null) {
return outState;
}
//创建一个容器,保存View的状态
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);//把结果保存到Bundle中

// 找到有焦点的View,保存它的状态
View focusedView = mContentParent.findFocus();
if (focusedView != null) {
if (focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
} else {
if (false) {
Log.d(TAG, "couldn't save which view has focus because the focused view "
+ focusedView + " has no id.");
}
}
} ......最后return outState;


接下来我们看saveHierarchyState方法,由于ViewGroup没这个方法,所以我们去View里看



public void saveHierarchyState(SparseArray<Parcelable> container) {//这个方法的意思是把View的状态保存到指定的容器中
dispatchSaveInstanceState(container);
}

然后我们看看dispatchSaveInstanceState方法

protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {//判断View是否有id以及是否允许保存状态
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();//获取View保存的状态结果,如果是自定View我们需要重写onSaveInstanceState方法
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);//保存View的状态,键是id
}
}
}


从上面的代码可以看出,一个View如果想要保存状态,则必须提供一个id,并且它是允许状态保存的,我们可以通过setSaveEnabled方法来决定是否能够保存

接下来我们看看ViewGroup的dispatchSaveInstanceState方法

protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
super.dispatchSaveInstanceState(container);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {//遍历子View,通知它们保存状态
View c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
c.dispatchSaveInstanceState(container);
}
}
}


到此,整个View的保存状态就完了,接下来我i们看看Fragment的保存,在Activity的onSaveInstanceState中有这样的代码。



Parcelable p = mFragments.saveAllState();

mFragments是FragmentController的实例



public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}

最后到了FragmentManagerImpl的saveAllState方法



Parcelable saveAllState() {
//获取所有活动中Fragment的数量
int N = mActive.size();
FragmentState[] active = new FragmentState[N];//创建对应数量的FragmentState数组
boolean haveFragments = false;
for (int i=0; i<N; i++) {
Fragment f = mActive.get(i);//遍历获取每个Fragment
....
FragmentState fs = new FragmentState(f);//根据Fragment创建对应的FragmentState对象
active[i] = fs;

if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = saveFragmentBasicState(f);//将Fragment保存的状态结果赋值给FragmentState的mSavedFragmentState属性

if (f.mTarget != null) {
if (f.mTarget.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: " + f
+ " has target not in fragment manager: " + f.mTarget));
}
if (fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = new Bundle();
}
putFragment(fs.mSavedFragmentState,
FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
if (f.mTargetRequestCode != 0) {
fs.mSavedFragmentState.putInt(
FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
f.mTargetRequestCode);
}
}

} else {
fs.mSavedFragmentState = f.mSavedFragmentState;
}

if (DEBUG) Log.v(TAG, "Saved state of " + f + ": "
+ fs.mSavedFragmentState);
}
}

if (!haveFragments) {
if (DEBUG) Log.v(TAG, "saveAllState: no fragments!");
return null;
}

int[] added = null;
BackStackState[] backStack = null;

// Build list of currently added fragments.
if (mAdded != null) {
N = mAdded.size();
if (N > 0) {
added = new int[N];
for (int i=0; i<N; i++) {
added[i] = mAdded.get(i).mIndex;
if (added[i] < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + mAdded.get(i)
+ " has cleared index: " + added[i]));
}
if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
+ ": " + mAdded.get(i));
}
}
}

// Now save back stack.
if (mBackStack != null) {
N = mBackStack.size();
if (N > 0) {
backStack = new BackStackState[N];
for (int i=0; i<N; i++) {
backStack[i] = new BackStackState(this, mBackStack.get(i));
if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + mBackStack.get(i));
}
}
}

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


FragmentState代表着Fragment的状态,其中有个属性mSavedFragmentState就保存了我们Fragment的状态



final class FragmentState implements Parcelable {
final String mClassName;
final int mIndex;
final boolean mFromLayout;
final int mFragmentId;
final int mContainerId;
final String mTag;
final boolean mRetainInstance;
final boolean mDetached;
final Bundle mArguments;

Bundle mSavedFragmentState;//保存的Fragment状态

这次是将saveFragmentBasicState方法的返回值赋值给了mSavedFragmentState,我们看看这个方法



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

if (mStateBundle == null) {
mStateBundle = new Bundle();
}
f.performSaveInstanceState(mStateBundle);//执行Fragment状态的保存
if (!mStateBundle.isEmpty()) {
result = mStateBundle;
mStateBundle = null;
}

if (f.mView != null) {
saveFragmentViewState(f);//保存Fragment的视图树
}
if (f.mSavedViewState != null) {
if (result == null) {
result = new Bundle();
}
result.putSparseParcelableArray(//把视图树保存的结构保存到Bundle中
FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
}
if (!f.mUserVisibleHint) {
if (result == null) {
result = new Bundle();
}
result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
}

return result;
}


我们看看performSaveInstanceState方法



void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
if (mChildFragmentManager != null) {
Parcelable p = mChildFragmentManager.saveAllState();
if (p != null) {
outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
}
}
}

这里调用了onSaveInstanceState方法,这是一个空方法,我们在这个方法里可以保存Fragment的状态,然后我们看


saveFragmentViewState方法,这是保存Fragment的视图树

void saveFragmentViewState(Fragment f) {
if (f.mView == null) {
return;
}
if (mStateArray == null) {
mStateArray = new SparseArray<Parcelable>();
} else {
mStateArray.clear();
}
f.mView.saveHierarchyState(mStateArray);//通知View保存视图数状态,回到了View的保存过程
if (mStateArray.size() > 0) {
f.mSavedViewState = mStateArray;
mStateArray = null;
}
}


到此,Fragment状态的保存也分析完了。


总结

通过上面的分析,我们发现在saveFragmentBasicState方法里主要做了两件事

1.保存Fragment的数据

2.保存Fragment的视图树

状态恢复流程分析

 上面分析完了是如何保存的,接下来我们看看恢复数据的流程。



protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);//获取Activity的视图树的保存结果
if (windowState != null) {//根据之前保存的结果恢复状态
mWindow.restoreHierarchyState(windowState);
}
}
}
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}

SparseArray<Parcelable> savedStates//获取保存的View的状态的容器
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {//通知View恢复保存的状态
mContentParent.restoreHierarchyState(savedStates);
}

// restore the focused view
int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
if (focusedViewId != View.NO_ID) {
View needsFocus = mContentParent.findViewById(focusedViewId);
if (needsFocus != null) {
needsFocus.requestFocus();
} else {
Log.w(TAG,
"Previously focused view reported id " + focusedViewId
+ " during save, but can't be found during restore.");
}
}

// restore the panels
SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
if (panelStates != null) {
restorePanelState(panelStates);
}

if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates =
savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
if (actionBarStates != null) {
doPendingInvalidatePanelMenu();
mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
} else {
Log.w(TAG, "Missing saved instance states for action bar views! " +
"State will not be restored.");
}
}
}


mContentParent是一个ViewGroup,我们看看ViewGroup的restoreHierarchyState方法





public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID);//根据id获取对应的之前保存的数据
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state);//恢复数据
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}

View的状态恢复我们分析完了,接下来看看Fragmetn状态的恢复,我们在Activity的onCreate中可以发现这样的代码



if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);//获取Fragment之前保存的状态
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}

同样,最后来到了FragmentManagerImpl的restoreAllState方法



void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
FragmentManagerState fms = (FragmentManagerState)state;
if (fms.mActive == null) return;
......省去部分代码
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];//获取Fragment的状态
if (fs != null) {
Fragment f = fs.instantiate(mHost, mParent);//调用FragmentState的instantiate方法
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
// retained above), clear mInstance in case we end up re-restoring
// from this FragmentState again.
fs.mInstance = null;
} else {
mActive.add(null);
if (mAvailIndices == null) {
mAvailIndices = new ArrayList<Integer>();
}
if (DEBUG) Log.v(TAG, "restoreAllState: avail #" + i);
mAvailIndices.add(i);
}
}

// Update the target of all retained fragments.
if (nonConfig != null) {
for (int i=0; i<nonConfig.size(); i++) {
Fragment f = nonConfig.get(i);
if (f.mTargetIndex >= 0) {
if (f.mTargetIndex < mActive.size()) {
f.mTarget = mActive.get(f.mTargetIndex);
} else {
Log.w(TAG, "Re-attaching retained fragment " + f
+ " target no longer exists: " + f.mTargetIndex);
f.mTarget = null;
}
}
}
}

 我们看看instantiate方法


public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
if (mInstance != null) {
return mInstance;
}

final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
//创建一个Fragment对象
mInstance = Fragment.instantiate(context, mClassName, mArguments);

if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;//把之前保存的Fragment的状态赋值给新建的Fragment对象
}
mInstance.setIndex(mIndex, parent);
mInstance.mFromLayout = mFromLayout;
mInstance.mRestored = true;
mInstance.mFragmentId = mFragmentId;
mInstance.mContainerId = mContainerId;
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);

return mInstance;
}


这里只是恢复了Fragment的一部分的数据,View视图树还没创建,所以别的数据是在Fragment进入生命周期才恢复的,


我们再回到Activity的onCreate中,调用了mFragments.dispatchCreate();方法,此时调用的是FragmentManager的方法


public void dispatchCreate() {
mStateSaved = false;
moveToState(Fragment.CREATED, false);
}

最后到了moveToState方法





void moveToState(Fragment f, int newState, int transit, int transitionStyle,
boolean keepActive) {
case Fragment.CREATED:
if (newState > Fragment.CREATED) {
if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ f.getResources().getResourceName(f.mContainerId)
+ ") for fragment " + f));
}
}
f.mContainer = container;//获取onCreateView的返回值
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
Animator anim = loadAnimator(f, transit, true,
transitionStyle);
if (anim != null) {
anim.setTarget(f.mView);
setHWLayerAnimListenerIfAlpha(f.mView, anim);
anim.start();
}
container.addView(f.mView);
}
if (f.mHidden) f.mView.setVisibility(View.GONE);
f.onViewCreated(f.mView, f.mSavedFragmentState);
}
}
//调用Fragment的onActivityCreated方法
f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {//恢复数据
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}

从上面的代码我们可以看出,恢复Fragment数据的操作是在onActivityCreate方法之后,




总结



在Fragment中我们可以通过onSaveInstanceState方法保存数据,需要注意的是,


Fragment并没有onRestoreInStanceState方法,我们可以在onActivityCreated和重写onViewStateRestored方法恢复数据,却别时


onActivityCreate方法执行的比较早。



public class CustomFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}

@Override//在此可以恢复保存的数据
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}

@Override//保存数据
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}

@Override//在此也可以恢复数据
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
}


自定义View状态的保存和恢复


 


public class CustomView extends ViewGroup {
private int mCurrentPosition;
public CustomView(Context context) {
super(context);
}

public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
//是否允许保存状态
setSaveEnabled(true);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

}

@Override
protected Parcelable onSaveInstanceState() {
//获取Parcelable对象
Parcelable parcelable = super.onSaveInstanceState();
SaveState saveState = new SaveState(parcelable);
//保存当前的位置
saveState.currentPosition = mCurrentPosition;
return saveState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
SaveState saveState = (SaveState) state;
//恢复父类的状态
super.onRestoreInstanceState(saveState.getSuperState());
//恢复我们自定义的属性
setCurrentPosition(saveState.currentPosition);
}
//继承BaseSavedState的目的是保存上一级状态的同时允许我们保存自定义的属性
static class SaveState extends BaseSavedState{
private int currentPosition;
public SaveState(Parcel source) {
super(source);
currentPosition = source.readInt();
}
//此构造方法用于创建对象
public SaveState(Parcelable superState) {
super(superState);
}

@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(currentPosition);
}

public static final Parcelable.Creator<SaveState> CREATOR = new Creator<SaveState>() {
@Override
public SaveState createFromParcel(Parcel source) {
return new SaveState(source);
}

@Override
public SaveState[] newArray(int size) {
return new SaveState[size];
}
};
}

public void setCurrentPosition(int position){
mCurrentPosition = position;
}
}

我们再考虑一种情况,加入我们写了一个自定义的容器,然后在Activity的布局中两次引入我们自定义的容器,当我们旋转屏幕以后,会发现恢复状态后两个View的状态是一样的。为什么呢?因为状态的保存是依靠View的id,我们保存view状态的容器是全局共享的,这个容器是在PhoneWindow的saveHierarchyState方法中定义的



@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);


SparseArray以View的id为键,状态为值进行保存,如果View的id一样,则恢复的时候一最后存储的View的状态进行恢复。




那么如何解决这个问题,答案是为每个子View具有独立的SparseArray容器来保存状态,这样就不会重叠了。代码如下



public class MyCustomLayout extends LinearLayout {
public MyCustomLayout(Context context) {
this(context, null);
}

public MyCustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setSaveEnabled(true);//允许View保存自己的状态
}

@Nullable
@Override
protected Parcelable onSaveInstanceState() {
//存储父类状态
Parcelable parcelable = super.onSaveInstanceState();
SaveState saveState = new SaveState(parcelable);
saveState.childrenState = new SparseArray<>();
//存储每个子View的状态到独立的SparseArray中
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).saveHierarchyState(saveState.childrenState);
}
return saveState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
SaveState saveState = (SaveState) state;
//恢复父类状态
super.onRestoreInstanceState(saveState.getSuperState());
//从容器中恢复每个ziView状态
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).restoreHierarchyState(saveState.childrenState);
}
}

/**
* 重写dispatchSaveInstanceState方法,在ViewGroup中这个方法是保存自己以及子View的状态
* 这里我们已经手动保存了子View的状态,所以不需要再去保存
* 我们调用dispatchFreezeSelfOnly只保存自己的状态
*/
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
dispatchFreezeSelfOnly(container);
}

/**
* dispatchRestoreInstanceState,在ViewGroup中这个方法是恢复自己以及子View的状态
* 这里我们已经手动恢复了子View的状态,所以不需要再去恢复
* 我们调用dispatchThawSelfOnly只保存自己的状态
*/
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
dispatchThawSelfOnly(container);
}

static class SaveState extends BaseSavedState {
private SparseArray childrenState;

SaveState(Parcel source) {
super(source);

}

SaveState(Parcel source, ClassLoader loader) {
super(source);
childrenState = source.readSparseArray(loader);
}

SaveState(Parcelable superState) {
super(superState);
}

@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);


out.writeSparseArray(childrenState);


}

public static final ClassLoaderCreator<SaveState> CREATOR = new ClassLoaderCreator<SaveState>() { @Override public SaveState createFromParcel(Parcel source, ClassLoader loader) { return new SaveState(source, loader); } @Override public SaveState createFromParcel(Parcel source) { return new SaveState(source); } @Override public SaveState[] newArray(int size) { return new SaveState[size]; } }; } }