基于Androidx+Fragment源码分析fragment重叠的原因

本文基于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时找不到对应的实例对象。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值