本文基于implementation "androidx.fragment:fragment-ktx:1.3.1"源码,分析了为什么fragment以show/hide方式,在Activity内存不足重新打开时会出现重叠问题的原因,及解决思路。
首先查看Activity的super.onSaveInstanceState(outState)方法
最后会在Activity中的onSaveInstanceState方法中看到
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
如上代码,看看mFragments是什么
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
发现mFragments是FragmentController对象的引用,查看FragmentController是如何创建的
private final FragmentHostCallback<?> mHost;
/**
* Returns a {@link FragmentController}.
*/
public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
return new FragmentController(callbacks);
}
private FragmentController(FragmentHostCallback<?> callbacks) {
mHost = callbacks;
}
发现其通过静态createController方法,直接创建了FragmentController对象并返回,并将
FragmentHostCallback对象赋值给mHost在FragmentController保存了起来。
而HostCallback通过new的方式直接创建,查看HostCallback类发现
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
}
}
HostCallback类继承了FragmentHostCallback类,并在构造方法中传入了Activity实例,此时
FragmentHostCallback持有了Activity对象的引用
继续查看saveAllStete方法
/**
* Saves the state for all Fragments.
*/
public Parcelable saveAllState() {
return mHost.mFragmentManager.saveAllState();
}
其调用了mHost.mFragmentManager,FragmentManager的saveAllState方法
查看FragmentHostCallback类可知
final FragmentManager mFragmentManager = new FragmentManagerImpl();
mFragmentManager对象是直接new出来的。现在查看FragmentManagerImpl类
class FragmentManagerImpl extends FragmentManager {
}
发现只是继承了FragmentManager
找到FragmentManager的saveAllSate方法
Parcelable saveAllState() {
// Make sure all pending operations have now been executed to get
// our state update-to-date.
forcePostponedTransactions();
endAnimatingAwayFragments();
execPendingActions(true);
mStateSaved = true;
mNonConfig.setIsStateSaved(true);
// First collect all active fragments.首先收集全部活动的fragments
ArrayList<FragmentState> active = mFragmentStore.saveActiveFragments();
if (active.isEmpty()) {
if (isLoggingEnabled(Log.VERBOSE)) Log.v(TAG, "saveAllState: no fragments!");
return null;
}
// Build list of currently added fragments.创建当前已经添加的fragments集合
ArrayList<String> added = mFragmentStore.saveAddedFragments();
// Now save back stack.保存回退栈
BackStackState[] backStack = null;
if (mBackStack != null) {
int size = mBackStack.size();
if (size > 0) {
backStack = new BackStackState[size];
for (int i = 0; i < size; i++) {
backStack[i] = new BackStackState(mBackStack.get(i));
if (isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "saveAllState: adding back stack #" + i
+ ": " + mBackStack.get(i));
}
}
}
}
FragmentManagerState fms = new FragmentManagerState();
fms.mActive = active;
fms.mAdded = added;
fms.mBackStack = backStack;
fms.mBackStackIndex = mBackStackIndex.get();
if (mPrimaryNav != null) {
fms.mPrimaryNavActiveWho = mPrimaryNav.mWho;
}
fms.mResultKeys.addAll(mResults.keySet());
fms.mResults.addAll(mResults.values());
fms.mLaunchedFragments = new ArrayList<>(mLaunchedFragments);
return fms;
}
上述代码保存了全部活动fragments的状态,及当前已经添加的fragments集合
还有回退栈的状态,并全部保存在了FragmentManagerState对象中,并且该对象实现了
Parcelable接口
然后
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
如果p!=null,就将该序列化后的对象保存在Bundle对象中。
再看Activity的onCreate方法
if (savedInstanceState != null) {
mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID,
View.LAST_APP_AUTOFILL_ID);
if (mAutoFillResetNeeded) {
getAutofillManager().onCreate(savedInstanceState);
}
//1
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
//2
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.fragments : null);
}
mFragments.dispatchCreate();
注释1:.从Bundle中取出先前在onSaveInstanceState方法中存入的序列化对象FragmentManagerState
注释2:mFragments就是FragmentController,调用了其restoreAllState方法,并传入了序列化对象p
public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
mHost.mFragmentManager.restoreAllState(state, nonConfig);
}
和上面一样,还是调用了FragmentManager的restoreAllstate方法,该方法有点长,主要看重点,
1.首先是把序列化的p强制类型转换为了原来的FragmentManagerState对象,
2.然后把原先存储在FragmentStore对象中的活动的fragments的清空,里面使用hashMap保存的
3.然后就是寻找存储在FragmentManagerState 对象中的fragments,并遍历找到对应的fragment,
恢复状态并使其活动,重新调用生命周期。
void restoreSaveState(@Nullable Parcelable state) {
// If there is no saved state at all, then there's nothing else to do
if (state == null) return;
//1.把序列化的p强制类型转换为了原来的FragmentManagerState对象
FragmentManagerState fms = (FragmentManagerState) state;
if (fms.mActive == null) return;
// Build the full list of active fragments, instantiating them from
// their saved state.
//2.然后把原先存储在FragmentStore对象中的活动的fragments的清空,里面使用hashMap保存的
mFragmentStore.resetActiveFragments();
//3.遍历获取fragment
for (FragmentState fs : fms.mActive) {
if (fs != null) {
FragmentStateManager fragmentStateManager;
Fragment retainedFragment = mNonConfig.findRetainedFragmentByWho(fs.mWho);
if (retainedFragment != null) {
if (isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "restoreSaveState: re-attaching retained "
+ retainedFragment);
}
fragmentStateManager = new FragmentStateManager(mLifecycleCallbacksDispatcher,
mFragmentStore, retainedFragment, fs);
} else {
fragmentStateManager = new FragmentStateManager(mLifecycleCallbacksDispatcher,
mFragmentStore, mHost.getContext().getClassLoader(),
getFragmentFactory(), fs);
}
//4.获取对应的fragment
Fragment f = fragmentStateManager.getFragment();
f.mFragmentManager = this;
if (isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "restoreSaveState: active (" + f.mWho + "): " + f);
}
//5.恢复状态
fragmentStateManager.restoreState(mHost.getContext().getClassLoader());
//6.使其活动
mFragmentStore.makeActive(fragmentStateManager);
// Catch the FragmentStateManager up to our current state
// In almost all cases, this is Fragment.INITIALIZING, but just in
// case a FragmentController does something...unique, let's do this anyways.
//7.重置生命周期
fragmentStateManager.setFragmentManagerState(mCurState);
}
}
// Check to make sure there aren't any retained fragments that aren't in mActive
// This can happen if a retained fragment is added after the state is saved
for (Fragment f : mNonConfig.getRetainedFragments()) {
if (!mFragmentStore.containsActiveFragment(f.mWho)) {
if (isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "Discarding retained Fragment " + f
+ " that was not found in the set of active Fragments " + fms.mActive);
}
mNonConfig.removeRetainedFragment(f);
// We need to ensure that onDestroy and any other clean up is done
// so move the Fragment up to CREATED, then mark it as being removed, then
// destroy it without actually adding the Fragment to the FragmentStore
f.mFragmentManager = this;
FragmentStateManager fragmentStateManager = new FragmentStateManager(
mLifecycleCallbacksDispatcher, mFragmentStore, f);
fragmentStateManager.setFragmentManagerState(Fragment.CREATED);
fragmentStateManager.moveToExpectedState();
f.mRemoving = true;
fragmentStateManager.moveToExpectedState();
}
}
// Build the list of currently added fragments.
mFragmentStore.restoreAddedFragments(fms.mAdded);
// Build the back stack.
if (fms.mBackStack != null) {
mBackStack = new ArrayList<>(fms.mBackStack.length);
for (int i = 0; i < fms.mBackStack.length; i++) {
BackStackRecord bse = fms.mBackStack[i].instantiate(this);
if (isLoggingEnabled(Log.VERBOSE)) {
Log.v(TAG, "restoreAllState: back stack #" + i
+ " (index " + bse.mIndex + "): " + bse);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
bse.dump(" ", pw, false);
pw.close();
}
mBackStack.add(bse);
}
} else {
mBackStack = null;
}
mBackStackIndex.set(fms.mBackStackIndex);
if (fms.mPrimaryNavActiveWho != null) {
mPrimaryNav = findActiveFragment(fms.mPrimaryNavActiveWho);
dispatchParentPrimaryNavigationFragmentChanged(mPrimaryNav);
}
ArrayList<String> savedResultKeys = fms.mResultKeys;
if (savedResultKeys != null) {
for (int i = 0; i < savedResultKeys.size(); i++) {
mResults.put(savedResultKeys.get(i), fms.mResults.get(i));
}
}
mLaunchedFragments = new ArrayDeque<>(fms.mLaunchedFragments);
}
总结:
通过跟踪源码可以发现,在Activity因内存不足被系统回收时,Activity会走onSaveInstanceState方法,而该方法会保存当前所有fragment及其状态,再重新打开该Activity时,走onCreate方法时,会判断bundle是否为null,如果不为null,就会取出保存的fragment并重置其状态。
如果我们在Activity的onCreate方法中不做任何处理,就会重新创建多个fragment的实例并重新add/commit,这时就会出现fragment的重叠。
解决方案:
如果我们在onCreate方法中判断当bundle不为null时,取出Activity帮我们保存的对应fragment实例,在onSaveInstanceState方法中保存选中的fragment的下标,来重新设置fragment的显示隐藏,这样就不会出现fragment的重叠的问题了。
还有一点需要注意的是,当我们取出fragment的实例为null时,我们需要重新创建fragment的实例,避免在切换fragment时找不到对应的实例对象。