深入理解fragment事务提交流程

I . 介绍

FragmentManager和FragmentTransaction是Android中用于管理和操作Fragment事务的重要类之一。它提供了一系列方法,用于执行Fragment的添加、替换、移除以及回滚等操作。

通过深入理解FragmentManager和FragmentTransaction的源码,更好地掌握Fragment事务的管理和操作。

在安卓开发中,FragmentManager和FragmentTransaction是核心的API,用于管理和操作Fragment的交互和展示。了解源码和实现细节,对于安卓开发工程师至关重要。

  • 本文旨在深入解析FragmentManager和FragmentTransaction的源码和实现细节,帮助读者更好地理解和应用该API。
  • 我们将探讨事务的创建和提交过程,以及Fragment的添加、替换和移除操作的实现。
  • 此外,我们还将研究事务回滚流程的实现。
  • 通过本文的阅读,将能够更好地理解FragmentManager和FragmentTransaction的工作原理,进而提升自己在安卓开发领域的技能和水平。

II. 核心类和事物创建的代码

源码结构和核心类的功能

  • FragmentTransaction的源码结构主要由以下核心类组成:

    • FragmentTransaction:该类是Fragment事务的顶层类,提供了事务操作的入口和控制方法。
    • BackStackRecord:该类继承自FragmentTransaction,负责管理Fragment事务的回滚和撤销操作。它维护了一个Fragment事务的回退栈,允许开发者在需要时回滚或撤销之前的事务
    • FragmentManagerImpl:该类是FragmentManager的实现类,负责管理Fragment的生命周期和事务。在FragmentTransaction中,通过FragmentManagerImpl来执行具体的事务操作。
    • FragmentTransactionOp:该类用于描述Fragment事务的操作,每个事务操作都可以看作是一个FragmentTransactionOp的实例。它记录了操作的类型、Fragment对象以及其他相关信息。
    • 这些核心类相互协作,实现了对Fragment的添加、替换和移除等操作的管理和控制。通过深入理解它们的功能和源码实现,我们能更好地理解FragmentTransaction的工作原理。

事务的创建和提交过程

kotlin 复制代码
val transaction = fm.beginTransaction()
transaction.add(R.id.main, fragment,tag)
//transaction.remove(fragment)
//transaction.replace(R.id.main, fragment,tag)
transaction.commit()

@NonNull
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}
  • 创建事务:首先,通过FragmentManager的beginTransaction()方法获取一个FragmentTransaction实例,通过该实例进行后续的事务操作。
  • 添加、替换或移除Fragment:调用FragmentTransaction的add()、replace()、remove()等方法,传入相应的Fragment对象和容器ID,来执行对应的操作。
  • 设置事务动画:通过调用FragmentTransaction的setCustomAnimations()方法,可以为事务设置自定义的进出栈动画和过渡效果。
  • 将事务添加到回退栈:通过调用FragmentTransaction的addToBackStack()方法,可以将事务添加到回退栈中,以便在需要时进行回滚或撤销。
  • 提交事务:调用FragmentTransaction的commit()方法,将事务提交给FragmentManagerImpl执行。提交后,FragmentManagerImpl会根据事务的操作列表逐个执行相应的操作。

添加、替换和移除操作的实现

kotlin 复制代码
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment,
                               @Nullable String tag) {
    doAddOp(containerViewId, fragment, tag, OP_ADD);
    return this;
}

void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
    ......
    if (tag != null) {
        fragment.mTag = tag;
    }
 
    if (containerViewId != 0) {
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }

    addOp(new Op(opcmd, fragment));
}

void addOp(Op op) {
    mOps.add(op);
    op.mEnterAnim = mEnterAnim;
    op.mExitAnim = mExitAnim;
    op.mPopEnterAnim = mPopEnterAnim;
    op.mPopExitAnim = mPopExitAnim;
}

添加、替换和移除操作的源码如上,所有的操作最后都走到了doAddOp()方法中,将tag和containerId存储到fragment之后新建FragmentTransactionOp对象,并且存储到队列中。

III. 事务提交操作流程的源码实现

从commit方法开始,探索fragment最后如何添加上去的。

FragmentTransaction中commit()源码

kotlin 复制代码
@Override
public int commit() {
    return commitInternal(false);
}

@Override
public int commitAllowingStateLoss() {
    return commitInternal(true);
}

int commitInternal(boolean allowStateLoss) {
    if (mCommitted) throw new IllegalStateException("commit already called");

    mCommitted = true;
    ...
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

@Override
public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
                           @NonNull ArrayList<Boolean> isRecordPop) {

    records.add(this);
    isRecordPop.add(false);
    if (mAddToBackStack) {
        mManager.addBackStackState(this);
    }
    return true;
}

interface OpGenerator {
    boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
                        @NonNull ArrayList<Boolean> isRecordPop);
}
  • commit调用如上,commitAllowingStateLoss()和commit()的区别就是传入了一个不同的布尔值,最后都调用了commitInternal()。
  • commitInternal()方法里面拦截了重复的提交,之后调用FragmentManager的方法enqueueAction(),这里传给enqueueAction的对象是OpGenerator。
  • OpGenerator的实现generateOps()方法将自身添加到任务列表,在commit的之后执行逻辑的时候这个方法会被调用。
  • 调用FragmentManager的方法enqueueAction(),接下来看它的源码。

FragmentManager中enqueueAction()源码

kotlin 复制代码
void enqueueAction(@NonNull OpGenerator action, boolean allowStateLoss) {
    if (!allowStateLoss) {
        if (mHost == null) {
            if (mDestroyed) {
                throw new IllegalStateException("FragmentManager has been destroyed");
            } else {
                throw new IllegalStateException("FragmentManager has not been attached to a "
                        + "host.");
            }
        }
        checkStateLoss();
    }
    synchronized (mPendingActions) {
        ...
        mPendingActions.add(action);
        scheduleCommit();
    }
}

void scheduleCommit() {
    synchronized (mPendingActions) {
        ...
        boolean pendingReady = mPendingActions.size() == 1;
        if (pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit);
        }
    }
}

private Runnable mExecCommit = new Runnable() {
    @Override
    public void run() {
        execPendingActions(true);
    }
};
  • enqueueAction()方法中,首先进行的状态检测,前面传入的allowStateLoss如果为false,就检测fragment是否已经销毁,或者是不是已经保存了状态。如果在保存状态之后提交了操作,在恢复的时候可能会丢失掉这些提交,所以在commit的时候拦截并且抛出异常,如果想要跳过这个检查,调用commitAllowingStateLoss()即可。

  • 之后把事务添加到列表,然后调用scheduleCommit()启动事务,这里可以注意到启动调用了Handler的post(),所以commit之后实际的处理是延迟的,时间是不确定的,这是一个特别重要的特性,在大多数时候可能都被忽略了,但在处理一些时间先后特别重要的事情的时候,如果忽略掉这个延迟,很大概率会发生不可预料的意外情况。

  • 延迟之后回调到execPendingActions()。

FragmentManager中execPendingActions()源码

kotlin 复制代码
boolean execPendingActions(boolean allowStateLoss) {

    boolean didSomething = false;
    while (generateOpsForPendingActions(mTmpRecords, mTmpIsPop)) {
        try {
            removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop);
        }
        didSomething = true;
    }

    return didSomething;
}

private boolean generateOpsForPendingActions(@NonNull ArrayList<BackStackRecord> records,
                                             @NonNull ArrayList<Boolean> isPop) {
    boolean didSomething = false;
    synchronized (mPendingActions) {
        if (mPendingActions.isEmpty()) {
            return false;
        }

        final int numActions = mPendingActions.size();
        for (int i = 0; i < numActions; i++) {
            didSomething |= mPendingActions.get(i).generateOps(records, isPop);
        }
        mPendingActions.clear();
        mHost.getHandler().removeCallbacks(mExecCommit);
    }
    return didSomething;
}

private void removeRedundantOperationsAndExecute(@NonNull ArrayList<BackStackRecord> records,
                                                 @NonNull ArrayList<Boolean> isRecordPop) {
    ...
    final int numRecords = records.size();
    int startIndex = 0;
    for (int recordNum = 0; recordNum < numRecords; recordNum++) {
        ...
        int reorderingEnd = recordNum + 1;
        executeOpsTogether(records, isRecordPop, recordNum, reorderingEnd);
        startIndex = reorderingEnd;
        recordNum = reorderingEnd - 1;
    }
}
  • 这里的方法开始源码逻辑太多,我对源码做了最大程度的删减,只保留了最简单情况下的调用流程。

  • execPendingActions()中循环的调用generateOpsForPendingActions()和removeRedundantOperationsAndExecute()方法,这里的循环也是处理一个特殊情况,为了更方便的理解,将这里的循环直接看作先后调用一下两个方法。

  • generateOpsForPendingActions()方法中,调用commit时传入的OpGenerator对象的generateOps()方法,然后这里调用的removeCallbacks(),这个方法现在看来是无用的,等会源码到另一个地方,就会知道为什么这里需要remove。

  • generateOps()方法的源码,可以再回到前面commit部分再看下,在其中将自身作为BackStackRecord对象存入了列表中。虽然还有添加了BackStack的代码,但我们先忽略它。

  • removeRedundantOperationsAndExecute()方法做了比较多的删减,最简洁的代码,就是一个循环,对列表中每一个BackStackRecord执行executeOpsTogether()方法。但这还是太复杂,因为他是一个列表,我们再假设这个列表只有一个元素,因为我们前面正常的调用只创建和提交了一次。

FragmentManager中executeOpsTogether()源码

kotlin 复制代码
private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records,
                                @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    final boolean allowReordering = records.get(startIndex).mReorderingAllowed;
    boolean addToBackStack = false;
    ...
    //step1
    mTmpAddedFragments.addAll(mFragmentStore.getFragments());
    Fragment oldPrimaryNav = getPrimaryNavigationFragment();
    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        final BackStackRecord record = records.get(recordNum);
        final boolean isPop = isRecordPop.get(recordNum);
        if (!isPop) {
            oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
        }
    }
    mTmpAddedFragments.clear();
    
    //step2
    executeOps(records, isRecordPop, startIndex, endIndex);

    if (USE_STATE_MANAGER) {
        // The last operation determines the overall direction, this ensures that operations
        // such as push, push, pop, push are correctly considered a push
        boolean isPop = isRecordPop.get(endIndex - 1);
        // Ensure that Fragments directly affected by operations
        // are moved to their expected state in operation order
        for (int index = startIndex; index < endIndex; index++) {
            BackStackRecord record = records.get(index);
                //step3
                for (FragmentTransaction.Op op : record.mOps) {
                    Fragment fragment = op.mFragment;
                    if (fragment != null) {
                        FragmentStateManager fragmentStateManager =
                                createOrGetFragmentStateManager(fragment);
                        fragmentStateManager.moveToExpectedState();
                    }
                }
        }
        // And only then do we move all other fragments to the current state
        //step4
        moveToState(mCurState, true);
    }
    
}

//FragmentManager
private static void executeOps(@NonNull ArrayList<BackStackRecord> records,
                               @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    for (int i = startIndex; i < endIndex; i++) {
        final BackStackRecord record = records.get(i);
        record.executeOps();
    }
}

//BackStackRecord
void executeOps() {
    final int numOps = mOps.size();
    for (int opNum = 0; opNum < numOps; opNum++) {
        final Op op = mOps.get(opNum);
        final Fragment f = op.mFragment;
        switch (op.mCmd) {
            case OP_ADD:
                f.setNextAnim(op.mEnterAnim);
                mManager.setExitAnimationOrder(f, false);
                mManager.addFragment(f);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.mExitAnim);
                mManager.removeFragment(f);
                break;
            ...
        }
    }
}

//FragmentManager
void addFragment(@NonNull Fragment fragment) {
    FragmentStateManager fragmentStateManager = createOrGetFragmentStateManager(fragment);
    fragment.mFragmentManager = this;
    mFragmentStore.makeActive(fragmentStateManager);
    if (!fragment.mDetached) {
        mFragmentStore.addFragment(fragment);
    }
}
  • 这里还是删除了大量的代码,按照刚才提到的假设这个列表只有一个元素,就是我们最开始创建的BackStackRecord,并且isPop参数也为false,并且没有设置回退相关的属性。
  • 第一步,调用BackStackRecord的expandOps(),这个方法一方面根据操作计算出新的oldPrimaryNav,可以理解为当前展示的fragment,例如remove的fragment如果是展示的fragment就需要返回空;另一方面将操作拆解成更基础的操作,比如replace操作需要拆解为oldPrimaryNav的remove和新的fragment的add。具体的代码因为篇幅问题不再贴出来。
  • 第二步,FragmentManager的方法executeOps开始执行操作,调用到BackStackRecord的executeOps方法,其中根据不同的分类执行不同的操作,比如add调用FragmentManager的addFragment方法,remove调用FragmentManager的removeFragment方法。addFragment通过FragmentStore对象添加,不再展开。
  • 第三步,对于操作列表中的fragment,调用FragmentStateManager的moveToExpectedState方法,将fragment流转到所对应的生命周期。
  • 第四步,对所有已经添加的fragment流转生命周期,主要针对不再操作列表中的fragment,也是调用到FragmentStateManager的moveToExpectedState方法。

如何让事务同步执行

在调用了FragmentTransaction的commit方法之后,任务被添加到了handler中延迟去执行,但如果我们需要它立即去执行的话,按照前面的源码去理解,直接调用Runnable中回调的execPendingActions()去主动触发事务的处理,处理的过程中源码中已经将Handler回调清除掉,并且FragmentStateManager源码中提供了executePendingTransactions()方法供直接使用,这个方法会将所有的FragmentTransaction事务全部执行,在commit之后立即执行executePendingTransactions()方法,就可以同步的处理事务。

kotlin 复制代码
public boolean executePendingTransactions() {
    boolean updates = execPendingActions(true);
    forcePostponedTransactions();
    return updates;
}

另外FragmentTransaction还提供了commitNow()和commitNowAllowingStateLoss()方法,commitNow()调用到FragmentManager的execSingleAction()方法,源码如下:

kotlin 复制代码
@Override
public void commitNow() {
    disallowAddToBackStack();
    mManager.execSingleAction(this, false);
}

void execSingleAction(@NonNull 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();
        }
    }
}
  • execSingleAction()方法中,也都是熟悉的代码,跟前面的execPendingActions()方法极其类似,区别就是这里只是处理这一个FragmentTransaction的commit,对列表中的事务不做处理,就实现了将这一次的提交单独同步执行,不影响其他任务的排序。

Ⅳ. 事务回滚流程的源码实现

  • 在添加fragment的时候,可以设置属性让fragment可以回滚,比如在顶部fragment点击返回,会滚到上一个fragment。
  • 回滚操作是通过FragmentManager的popBackStack()方法实现的。直接上源码:

FragmentManager中popBackStack()源码

kotlin 复制代码
public void popBackStack() {
    enqueueAction(new PopBackStackState(null, -1, 0), false);
}

private class PopBackStackState implements OpGenerator {
    @Override
    public boolean generateOps(@NonNull ArrayList<BackStackRecord> records,
            @NonNull ArrayList<Boolean> isRecordPop) {
        return popBackStackState(records, isRecordPop, mName, mId, mFlags);
    }
}

boolean popBackStackState(@NonNull ArrayList<BackStackRecord> records,
        @NonNull ArrayList<Boolean> isRecordPop, @Nullable String name, int id, int flags) {
    if (mBackStack == null) {
        return false;
    }
    if (name == null && id < 0 && (flags & POP_BACK_STACK_INCLUSIVE) == 0) {
        int last = mBackStack.size() - 1;
        if (last < 0) {
            return false;
        }
        records.add(mBackStack.remove(last));
        isRecordPop.add(true);
    }
    return true;
}
  • popBackStack()调用了enqueueAction,这个方法跟添加一样,传入OpGenerator对象,最终在execPendingActions方法中回调对数据进行处理。
  • 这里看到回调中进入到FragmentManager的popBackStackState()方法,在这里从mBackStack堆栈中弹出最后一个BackStackRecord,而mBackStack的添加就是我们在提交的时候设置了属性,就会保留在这里。
  • 这里弹出BackStackRecord之后注意isRecordPop设置了true,后面的区别也都是来自这个属性。
  • execPendingActions执行完成后,根据前面看到的源码,下一步实际的操作到了executeOpsTogether中,前面的源码为了方便,去除了isRecordPop为true的源码,这里贴出来再看一次。

FragmentManager中executeOpsTogether()源码

kotlin 复制代码
private void executeOpsTogether(@NonNull ArrayList<BackStackRecord> records,
        @NonNull ArrayList<Boolean> isRecordPop, int startIndex, int endIndex) {
    for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
        final BackStackRecord record = records.get(recordNum);
        final boolean isPop = isRecordPop.get(recordNum);
        if (!isPop) {
            oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
        } else {
            oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
        }
        addToBackStack = addToBackStack || record.mAddToBackStack;
    }
    
    ...
    
    if (USE_STATE_MANAGER) {
        // The last operation determines the overall direction, this ensures that operations
        // such as push, push, pop, push are correctly considered a push
        boolean isPop = isRecordPop.get(endIndex - 1);
        // Ensure that Fragments directly affected by operations
        // are moved to their expected state in operation order
        for (int index = startIndex; index < endIndex; index++) {
            BackStackRecord record = records.get(index);
            if (isPop) {
                // Pop operations get applied in reverse order
                for (int opIndex = record.mOps.size() - 1; opIndex >= 0; opIndex--) {
                    FragmentTransaction.Op op = record.mOps.get(opIndex);
                    Fragment fragment = op.mFragment;
                    if (fragment != null) {
                        FragmentStateManager fragmentStateManager =
                                createOrGetFragmentStateManager(fragment);
                        fragmentStateManager.moveToExpectedState();
                    }
                }
            } else {
                for (FragmentTransaction.Op op : record.mOps) {
                    Fragment fragment = op.mFragment;
                    if (fragment != null) {
                        FragmentStateManager fragmentStateManager =
                                createOrGetFragmentStateManager(fragment);
                        fragmentStateManager.moveToExpectedState();
                    }
                }
            }

        }
        // And only then do we move all other fragments to the current state
        moveToState(mCurState, true);
    } 
}
  • 第一个区别点在与expandOps替换成了trackAddedFragmentsInPop方法,两个方法原理是一样的,计算当前显示的fragment,不过跟之前反了过来。
  • 第二个区别在生命周期同步的时候,顺序反过来,从最后一个开始同步。
  • 在executeOpsTogether之后,走到executeOps()开始处理回调,在其中也是替换了方法,正常走到BackStackRecord的executeOps方法,现在走到BackStackRecord的executePopOps方法。源码如下

3. FragmentManager中executeOps()源码

kotlin 复制代码
private static void executeOps(@NonNull ArrayList<BackStackRecord> records,
        @NonNull 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);
            // Only execute the add operations at the end of
            // all transactions.
            boolean moveToState = i == (endIndex - 1);
            record.executePopOps(moveToState);
        } else {
            record.bumpBackStackNesting(1);
            record.executeOps();
        }
    }
}

void executePopOps(boolean moveToState) {
    for (int opNum = mOps.size() - 1; opNum >= 0; opNum--) {
        final Op op = mOps.get(opNum);
        Fragment f = op.mFragment;
        switch (op.mCmd) {
            case OP_ADD:
                f.setNextAnim(op.mPopExitAnim);
                mManager.setExitAnimationOrder(f, true);
                mManager.removeFragment(f);
                break;
            case OP_REMOVE:
                f.setNextAnim(op.mPopEnterAnim);
                mManager.addFragment(f);
                break;
            ...
        }
    }

}
  • executePopOps首先不同的是也是从后往前遍历事务并操作,与提交的时候反过来。

  • 其中处理操作也是反过来的,比如add调用remove方法,remove调用add方法。

  • 通过这样的各种各样的反操作,将最顶部的fragment弹出,并且从屏幕中移除,因为这里处理的时候,已经是提交时解析过的事务,replace操作也已经拆解,所以反向操作的时候,也会把之前的fragment添加回来,完成了回滚操作。

以上就是整个提交的流程。

相关推荐
用户2018792831672 小时前
如何利用AI工具快速学习Android源码
android
音视频牛哥3 小时前
Android 平台RTSP/RTMP播放器SDK接入说明
android·音视频·大牛直播sdk·rtsp播放器·rtmp播放器·rtmp低延迟播放·rtmpplayer
aningxiaoxixi4 小时前
Android Framework 之 AudioDeviceBroker
android·windows·ffmpeg
~Yogi5 小时前
今日学习:工程问题(场景题)
android·学习
奔跑吧 android5 小时前
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
android·bluetooth·bt·aosp13
移动开发者1号5 小时前
Android Activity状态保存方法
android·kotlin
移动开发者1号5 小时前
Volley源码深度分析与设计亮点
android·kotlin
张风捷特烈5 小时前
每日一题 Flutter#7,8 | 关于 State 两道简答题
android·flutter·面试
计蒙不吃鱼13 小时前
一篇文章实现Android图片拼接并保存至相册
android·java·前端
LucianaiB14 小时前
如何做好一份优秀的技术文档:专业指南与最佳实践
android·java·数据库