Activity状态的恢复和保存

通常我们采用下面的方式保存Activity的状态

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}

接下来我们从源码分析一下

protected void onSaveInstanceState(Bundle outState) {
//通知mWindow去保存层级状态
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
//保存Fragment的状态
Parcelable p = mFragments.saveAllState();
if (p != null) {
//把数据放到Bundle里
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}

我们知道mWindow是PhoneWindow的实例,下面去看看

public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
//mContentParent是DecorView中一个id为content的容器,我们Activitiy中的布局
就是添加到这个mContent中
if (mContentParent == null) {
return outState;
}
//创建一个容器,用来保存View层级结构
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//保存View树状态
mContentParent.saveHierarchyState(states);
//将状态存到Bundle中
outState.putSparseParcelableArray(VIEWS_TAG, states);

           // 保存有焦点的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) {
dispatchSaveInstanceState(container);
}

protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//判断View是否有id以及是否允许保存
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
//获取保存的结果
Parcelable state = 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);
//把结过放到容器中,键为View的id,值为状态
container.put(mID, state);
}
}
}

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

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

@Override
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 c = children[i];
if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
//保存每个子View的状态
c.dispatchSaveInstanceState(container);
}
}
}

到此,整个View的保存状态就完了,接下来我i们看看Fragment的保存

Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}

最终调用到了FragmentManagerImpl的saveAllState方法,,
下面是精简后的代码

    if (mActive == null || mActive.size() <= 0) {
        return null;
    }
    
    //获得当前活动中的Fragment
    int N = mActive.size();

//创建对应的Fragment状态数据
FragmentState[] active = new FragmentState[N];
boolean haveFragments = false;
for (int i=0; i<N; i++) {
//获取活动中的每个Fragment
Fragment f = mActive.valueAt(i);
if (f != null) {
if (f.mIndex < 0) {
throwException(new IllegalStateException(
"Failure saving state: active " + f
+ " has cleared index: " + f.mIndex));
}

            haveFragments = true;
            //创建每个Fragment对应的状态对象FragmentState 
            FragmentState fs = new FragmentState(f);
            active[i] = fs;
            
            if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
              //代码1
                fs.mSavedFragmentState = saveFragmentBasicState(f);

                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);
        }
    }

在代码1处,把saveFragmentBasicState方法的返回值给了FragmentState对象的
mSavedFragmentState属性,这个属性是一个Bundle对象,下面我们看看saveFragmentBasicState方法

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

    if (mStateBundle == null) {
        mStateBundle = new Bundle();
    }

//代码2,调用Fragment对象的performSaveInstanceState方法
f.performSaveInstanceState(mStateBundle);
dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
if (!mStateBundle.isEmpty()) {
result = mStateBundle;
mStateBundle = null;
}

    if (f.mView != null) {

//保存Fragment视图数结构
saveFragmentViewState(f);
}
if (f.mSavedViewState != null) {
if (result == null) {
result = new Bundle();
}
//将上面保存的视图树的结果放到Bundle中
result.putSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
}
if (!f.mUserVisibleHint) {
if (result == null) {
result = new Bundle();
}
// Only add this if it's not the default value
result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
}

    return result;
}

代码2处条用了Fragment的一个方法,如下

void performSaveInstanceState(Bundle outState) {

//执行Fragment的onSaveInstanceState方法,这是一个空方法
onSaveInstanceState(outState);
if (mChildFragmentManager != null) {
Parcelable p = mChildFragmentManager.saveAllState();
if (p != null) {
outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
}
}
}

下面我们看保存Fragment视图数结构的代码

void saveFragmentViewState(Fragment f) {
if (f.mView == null) {
return;
}
if (mStateArray == null) {
mStateArray = new SparseArray<Parcelable>();
} else {
mStateArray.clear();
}
//和上面View的保存状态一样
f.mView.saveHierarchyState(mStateArray);
if (mStateArray.size() > 0) {
f.mSavedViewState = mStateArray;
mStateArray = null;
}
}

总结

通过上面的分析,我们发现在saveFragmentBasicState中主要做了两件事,
1.保存Fragment的数据
2.保存Fragment视图树结构

然后把这些数据给了FragmentState的一个Bundle属性,所以FragmentState这个对象对Fragment的状态保存很重要。

状态恢复源码分析

我们看Activity的onRestoreInstanceState方法

protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
//获取保存的视图树信息
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {

//调用PhoneWindow的restoreHierarchyState方法
mWindow.restoreHierarchyState(windowState);
}
}
}

PhoneWindow的方法
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
//获取保存的状态信息
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
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.");
        }
    }

调用了View的方法

public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}

protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
//判断当前View是否有id
if (mID != NO_ID) {
//根据id获取对应的数据
Parcelable state = container.get(mID);
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()");
}
}
}
}

恢复Fragment状态,我们看看Activity的onCreate方法
protected void onCreate(@Nullable Bundle savedInstanceState) {

if (savedInstanceState != null) {
//获取Fragment的数据
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
getApplication().dispatchActivityCreated(this, savedInstanceState);

下面我们看看restoreAllState方法,这个方法定义在FragmentManager中

void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
if (state == null) return;
FragmentManagerState fms = (FragmentManagerState)state;
if (fms.mActive == null) return;

for (int i=0; i<fms.mActive.length; i++) {
//获取Fragment的状态数据
FragmentState fs = fms.mActive[i];
Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.put(f.mIndex, 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;
}

我们去看FragmentState的instantiate方法

public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
Fragment parent, FragmentManagerNonConfig childNonConfig) {
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}

        if (container != null) {
            mInstance = container.instantiate(context, mClassName, mArguments);
        } else {
        //初始化Fragment
            mInstance = Fragment.instantiate(context, mClassName, mArguments);
        }
    //把之前保存的状态信息赋值给新建的对象
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(context.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
        }
        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.mHidden = mHidden;
        mInstance.mFragmentManager = host.mFragmentManager;
        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment " + mInstance);
    }
    mInstance.mChildNonConfig = childNonConfig;
    return mInstance;
}

这里只是恢复了Fragment的一部分的数据,View视图树还没创建,所以别的数据是在Fragment进入生命周期才恢复的,我们再回到Activity的onCreate中,调用了
mFragments.dispatchCreate();方法,此时调用的是FragmentManager的方法

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

private void dispatchMoveToState(int state) {
if (mAllowOldReentrantBehavior) {
moveToState(state, false);
} else {
try {
mExecutingActions = true;
moveToState(state, false);
} finally {
mExecutingActions = false;
}
}
execPendingActions();
}

然后看moveToState方法
void moveToState(int newState, boolean always) {
if (mAdded != null) {
final int numAdded = mAdded.size();
for (int i = 0; i < numAdded; i++) {
Fragment f = mAdded.get(i);
//看此方法
moveFragmentToExpectedState(f);
if (f.mLoaderManager != null) {
loadersRunning |= f.mLoaderManager.hasRunningLoaders();
}
}
}
}

void moveFragmentToExpectedState(final Fragment f) {
if (f == null) {
return;
}
int nextState = mCurState;
if (f.mRemoving) {
if (f.isInBackStack()) {
nextState = Math.min(nextState, Fragment.CREATED);
} else {
nextState = Math.min(nextState, Fragment.INITIALIZING);
}
}
//看此方法
moveToState(f, nextState, f.getNextTransition(), f.getNextTransitionStyle(), false);

在moveToState方法里调用了下面的方法
//调用Fragment的onActivityCreated方法
f.performActivityCreated(f.mSavedFragmentState);
dispatchOnFragmentActivityCreated(f, f.mSavedFragmentState, false);
if (f.mView != null) {
//恢复Fragment视图状态
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}

从上面的代码我们可以看出,恢复Fragment视图数据的时机是在onActivityCreated方法之后,

final void restoreViewState(Bundle savedInstanceState) {
if (mSavedViewState != null) {
//恢复View树状态
mView.restoreHierarchyState(mSavedViewState);
mSavedViewState = null;
}
mCalled = false;
onViewStateRestored(savedInstanceState);
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onViewStateRestored()");
}
}

@CallSuper
public void onViewStateRestored(Bundle savedInstanceState) {
mCalled = true;
}

总结

在Fragment中我们可以通过onSaveInstanceState方法保存数据,需要注意的是,Fragment并没有onRestoreInStanceState方法,我们一般重写onViewStateRestored方法恢复数据

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,835评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,900评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,481评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,303评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,375评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,729评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,877评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,633评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,088评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,443评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,563评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,251评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,827评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,712评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,943评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,240评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,435评论 2 348

推荐阅读更多精彩内容