深入理解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添加回来,完成了回滚操作。

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

相关推荐
Kapaseker5 小时前
你不看会后悔的2025年终总结
android·kotlin
alexhilton8 小时前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
ji_shuke8 小时前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
sunnyday042610 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理11 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台11 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐12 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极12 小时前
Android Jetpack Compose折叠屏感知与适配
android
HelloBan12 小时前
setHintTextColor不生效
android
洞窝技术14 小时前
从0到30+:智能家居配网协议融合的实战与思考
android