前言
遇到了一件很有趣的事情——在屏幕旋转之后,Fragment会被重复创建,于是我十分好奇Android是如何自动保存Fragment的状态并恢复的。网上有很多介绍onSaveInstanceState()
使用方法的文章,但是对这当中的细节探讨少之又少,故作此文以记录之。
灵异事件
众所周知,Activity和View都拥有onSaveInstanceState()
方法,Activity会递归调用各View的此方法并汇总数据保存到Bundle中再递交给相关system进程以方便在Activity重创建时恢复数据。
那么,Fragment呢?Fragment也是拥有子View的,必然,那些东西的数据也会被保存Bundle中,我们可以打印屏幕旋转后重建Fragment时的Bundle数据:
Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}], android:view_registry_state=Bundle[{androidx.lifecycle.BundlableSavedStateRegistry.key=Bundle[{}]}], android:view_state={2131231018=android.view.AbsSavedState$1@93a3522, 2131231019=android.view.AbsSavedState$1@93a3522, 2131231020=android.view.AbsSavedState$1@93a3522, 2131231021=android.view.AbsSavedState$1@93a3522, 2131231022=android.view.AbsSavedState$1@93a3522, 2131231023=android.view.AbsSavedState$1@93a3522}}]
可以看到,重建Fragment的Bundle中包含了其子View的数据,但是,我们打开Fragement的onSaveInstanceState()
方法:
public void onSaveInstanceState(Bundle outState) {
}
大受震撼,它居然是空的!
那么问题来了,Fragment并没有主动保存其子View的数据,那么这些数据是在哪里被保存的?
保存调用链
我们可以从Activity的onSaveInstanceState()
开始调查
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
dispatchActivitySaveInstanceState(outState);
}
Fragment寄生于Activity,可以看到,在其中,Activity会主动调用其FragmentController
的saveAllState()
方法,尝试保存数据。
而这个方法似乎只是个proxy。
public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}
最终会去调用FragmentManager
的saveAllState()
方法。
这个方法太长就不贴出来了,在其中会遍历所有active fragment,对其调用saveFragmentBasicState()
方法。
Bundle saveFragmentBasicState(Fragment f) {
Bundle result = null;
if (mStateBundle == null) {
mStateBundle = new Bundle();
}
f.performSaveInstanceState(mStateBundle);
dispatchOnFragmentSaveInstanceState(f, mStateBundle, false);
if (!mStateBundle.isEmpty()) {
result = mStateBundle;
mStateBundle = null;
}
if (f.mView != null) {
saveFragmentViewState(f);
}
if (f.mSavedViewState != null) {
if (result == null) {
result = new 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;
}
这里分为两个部分,一个是会调用该Fragment的performSaveInstanceState()
方法来递归到各个子Fragment。(具体看下面)
另一个则是调用saveFragmentViewState
来保存其View的状态。
void performSaveInstanceState(Bundle outState) {
onSaveInstanceState(outState);
if (mChildFragmentManager != null) {
Parcelable p = mChildFragmentManager.saveAllState();
if (p != null) {
outState.putParcelable(Activity.FRAGMENTS_TAG, p);
}
}
}
可以看到,在其中会去调用这个Fragment的子FragmentManager,并去调用其saveAllState()
,这就回到上面去了,也就是说它会一直递归直到解开所有Fragement套娃为止。
接着来看保存子View的方法:
void saveFragmentViewState(Fragment f) {
if (f.mView == null) {
return;
}
if (mStateArray == null) {
mStateArray = new SparseArray<Parcelable>();
} else {
mStateArray.clear();
}
f.mView.saveHierarchyState(mStateArray);
if (mStateArray.size() > 0) {
f.mSavedViewState = mStateArray;
mStateArray = null;
}
}
saveHierarchyState
,这个方法最终会调用到该View的onSaveInstanceState()
总结
Fragment本身并不负责保存其子View数据,这个过程由Activity调用FragmentManager
中的相关内容完成(虽然最终它们会被放到同一个Bundle中)