Fragment原理浅析

前言

Fragment在日常开发中非常的常用,一版都是配合ViewPager或FrameLayout使用,我们基本不用担心操作它attachToActivity,因为FragmentManager都帮我们处理好了。那么Fragment是如何绑定Activity的生命周期的呢?系统是如何将Fragment添加到视图层的呢?Fragment的回退栈又是什么呢?带着这些问题我们开始探索Fragment的源码吧。

基本操作

首先来回顾一下,我们如何添加Fragment的:

val fm = supportFragmentManager
val ts = fm.beginTransaction()
ts.add(fragment)
ts.commit()

首先弄清几个概念:

  • FragmentController 主要作用是绑定Activity与Fragment的生命周期,在FragmentActivity可以看到在每个生命周期函数,FragmentController都有做分发,最终交给了FragmentManager处理
  • FragmentManager Fragments的直接操作者,管理Fragment的内部状态以及添加\移除\隐藏\显示Fragment等操作
  • FragmentTransaction 对Fragment操作的集合,各项操作会存储在Ops中,最终在FragmentManager中被执行

tips: FragmentTransaction本身是一个抽象类,它包含着一个内部类Op,根据其构造函数可以看出来这个类用于记录Fragment的操作,并将这一系列操作存储在mOps,其中四个抽象方法commit/commitAllowingStateLoss/commitNow/commitNowAllowingStateLoss就是我们经常放在最后执行的方法了。

// fm.beginTransaction()
@NonNull
@Override
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}

BackStackRecord继承了FragmentTransaction,可以看到在这个类中最终还是调用了FragmentManager的enqueueAction方法,将所有的操作加入执行队列中。并对需要记录Fragment回退栈的操作做如下处理:

public int allocBackStackIndex(BackStackRecord bse) {
    synchronized (this) {
        if (mAvailBackStackIndices == null || mAvailBackStackIndices.size() <= 0) {
            if (mBackStackIndices == null) {
                mBackStackIndices = new ArrayList<BackStackRecord>();
            }
            int index = mBackStackIndices.size();
            if (DEBUG) Log.v(TAG, "Setting back stack index " + index + " to " + bse);
            // 将当前操作添加到数组中
            mBackStackIndices.add(bse);
            return index;
        } else {
            // 找到一个可用的位置进行存储当前操作
            int index = mAvailBackStackIndices.remove(mAvailBackStackIndices.size()-1);
            if (DEBUG) Log.v(TAG, "Adding back stack index " + index + " with " + bse);
            mBackStackIndices.set(index, bse);
            return index;
        }
    }
}

最终调用了FragmentManager的enqueueAction/execSingleAction:

public boolean execPendingActions() {
    // 校验准备工作
    ensureExecReady(true);
    boolean didSomething = false;
    // 初始化数据源
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        } finally {
            // 清除执行程序
            cleanupExec();
        }
        didSomething = true;
    }
    updateOnBackPressedCallbackEnabled();
    // 等待加载延迟的Fragment
    doPendingDeferredStart();
    burpActive();
    return didSomething;
}

public void execSingleAction(OpGenerator action, boolean allowStateLoss) {
    if (allowStateLoss && (mHost == null || mDestroyed)) {
        // This FragmentManager isn't attached, so drop the entire transaction.
        return;
    }
    ensureExecReady(allowStateLoss);
    if (action.generateOps(mTmpRecords, mTmpIsPop)) {
        mExecutingActions = true;
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        } finally {
            cleanupExec();
        }
    }

    updateOnBackPressedCallbackEnabled();
    doPendingDeferredStart();
    burpActive();
}

无论是执行execPendingActions还是execSingleAction,其核心方法还是removeRedundantOperationsAndExecute,这个方法可以移除冗余的操作,举个例子,如果两个事务一起执行,一个用于添加FragmentA,一个用于将FragmentA替换成FragmentB,实际上只有FragmentB会被添加,无法感应到FragmentA的创建/销毁生命周期。这个就是移除冗余操作的副作用了,Fragment的状态不会如预想那样变化。
疑问,这个方法是如何去除冗余操作的呢?

// 移除冗余的回退栈操作再执行,需要设置setReorderingAllowed(true)
private void removeRedundantOperationsAndExecute(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop) {
    // 省略...
    final int numRecords = records.size();
        int startIndex = 0;
        for (int recordNum = 0; recordNum < numRecords; recordNum++) {
            final boolean canReorder = records.get(recordNum).mReorderingAllowed;
            // 所有事务如果设置了setReorderingAllowed(true)则全部跳过在最后一起执行
            if (!canReorder) {
                // execute all previous transactions
                // 如果中间有事务A没有设置setReorderingAllowed(true),则从startIndex到事务A会被一起执行
                if (startIndex != recordNum) {
                    executeOpsTogether(records, isRecordPop, startIndex, recordNum);
                }
                // execute all pop operations that don't allow reordering together or one add operation
                // 上述注释说明此处执行所有不允许一起排序的pop操作
                // 在BackStackRecord中isRecordPop都为false,在PopBackStackState中isRecordPop都为true,这两个类分别对应着入栈和出栈,且仅当BackStackRecord设置了addToBackStack后才会被记录
                int reorderingEnd = recordNum + 1;
                if (isRecordPop.get(recordNum)) {
                    while (reorderingEnd < numRecords
                            && isRecordPop.get(reorderingEnd)
                            && !records.get(reorderingEnd).mReorderingAllowed) {
                        reorderingEnd++;
                    }
                }
                executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
                startIndex = reorderingEnd;
                recordNum = reorderingEnd - 1;
            }
        }
        if (startIndex != numRecords) {
            executeOpsTogether(records, isRecordPop, startIndex, numRecords);
        }
    }

接着往下看executeOps

private static void executeOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    for (int i = startIndex; i < endIndex; i++) {
        final BackStackRecord record = records.get(i);
        final boolean isPop = isRecordPop.get(i);
        if (isPop) {
             record.bumpBackStackNesting(-1);
            boolean moveToState = i == (endIndex - 1);
            // 执行PopBackStackState
            record.executePopOps(moveToState);
        } else {
            record.bumpBackStackNesting(1);
            // 执行BackStackRecord
            record.executeOps();
        }
    }
}

然后是BackStackRecord的executeOps,最终这些ops由FragmentManager处理,将Fragment添加至mAdded或者从mAdded中移除,并对Fragment的内部状态进行修改。

最后也是最重要的方法moveToState,它主要负责修改Fragment的生命周期状态,在这我们可以看到Fragment是如何被添加至容器中的,在此Fragment中内部状态通过FragmentManger更新。

case Fragment.CREATED:
    //省略...
    f.mContainer = container;
    f.performCreateView(f.performGetLayoutInflater(f.mSavedFragmentState), container, f.mSavedFragmentState);
    if (f.mView != null) {
        f.mInnerView = f.mView;
        f.mView.setSaveFromParentEnabled(false);
        if (container != null) {
            // 将fragment的视图添加到宿主的容器中
            container.addView(f.mView);
        }
        if (f.mHidden) {
            f.mView.setVisibility(View.GONE);
        }
        // 省略...
    } else {
        f.mInnerView = null;
    }
    // 省略...

至此,文章开始的疑问差不多都解决了,最后再梳理一下Fragment初始化流程。流程图大体如下:

结语

Fragment的逻辑复杂,如果仅仅是靠读源码,是无法理清其复杂的逻辑关系的。此文的目的只是对Fragment做一次简单的探索,弄清楚它是如何被添加到视图的,如何去感知Activity的生命周期的,至于它的高级用法以及使用注意事项将会发布在其后的文章。

评论