Fragment 的增、删源码解析

本文深入探讨了在Android中如何添加Fragment,从入口开始,详细解析了FragmentTransaction的add()方法、BackStackRecord类、真正的add()操作以及commit()方法的工作原理,揭示了Fragment管理的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、入口

一般我们添加一个 Fragment 的套路是这样的(xml 文件中不常不在我们讨论方法以内):
创建一个 Fragment 实例,获取 FragmentManager,然后通过 FragmentManager 获取去到一个 FragmentTransaction,然后通过 FragmentTransaction 的 add() 方法添加一个 Fragment,最后调用 FragmentTransaction 的 commit() 方法完成整个添加过程。
而在中间的 add() 方法中又有几种不同的方式,如:将当前添加的 Fragment 加入到 Fragment 回退栈中等。这个再跑套路中我们要一一分析,下面就开始吧。

二、开始讨论

Fragment_1 fragment_1 = new Fragment_1();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
1、add() 方法

我们点击进入到 FragmentTransaction 的 add() 方法中,add() 方法有三个,我们选择常用的,第一个参数为 int ,第二个参数为 Fragment 的方法查看,然后发现 FragmentTransaction 是个抽象类,那么我们回退一步,到 FragmentManager 获取 FragmentTransaction 的 beginTransaction() 方法中查看,FragmentManager 是一个接口,而他的实现者则是其一个内部类,FragmentMaanagerImpl ,我们查看其中的 beginTransaction() 方法。

@Override
public FragmentTransaction beginTransaction() {
      return new BackStackRecord(this);
}

这里直接返回了一个 BackStackRecord 实例,从命名上这个类是一个 回退栈的记录类,我们进去看一看,下面是这个类的一个局部代码。

/**
 * Entry of an operation on the fragment back stack.
 */
final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
    static final String TAG = FragmentManagerImpl.TAG;
    static final boolean SUPPORTS_TRANSITIONS = Build.VERSION.SDK_INT >= 21;

    final FragmentManagerImpl mManager;
    ......
    ......
    ......

这个是 FragmentTrnsaction 的子类,那么我们就清除了,我们再调用 FragmentTransaction 的 add() 方法时,其实调用的是 BackStackRecord 的 add() 方法。add() 方法我们稍后再看,先看这个类的初始化时都做了什么。

2、BackStackRecord 类
public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
    }

额,就做了一件事情,引用一个 FragmentManagerImpl 实例。那么我们还是去它的 add() 方法。

3、真 add() 方法
@Override
    public FragmentTransaction add(Fragment fragment, String tag) {
        doAddOp(0, fragment, tag, OP_ADD);
        return this;
    }

    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment) {
        doAddOp(containerViewId, fragment, null, OP_ADD);
        return this;
    }

    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
        doAddOp(containerViewId, fragment, tag, OP_ADD);
        return this;
    }

看到三个 add() 方法都是间接的调用了 doAAddOp() 这个方法,名字起的诡异,我们接着看。

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {       
        final Class fragmentClass = fragment.getClass();
        // 获取需要添加的 Fragment 类的修饰符
        final int modifiers = fragmentClass.getModifiers();
        // 如果该 Fragment 类,是匿名内部类、成员内部类、不是静态的、不是公共的都将抛出错误
        if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
                || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
            throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                    + " must be a public static class to be  properly recreated from"
                    + " instance state.");
        }

        fragment.mFragmentManager = mManager;
        // Fragment 可以重复使用,同一 Fragment 的 tag 不能相同
        if (tag != null) {
            if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
                throw new IllegalStateException("Can't change tag of fragment "
                        + fragment + ": was " + fragment.mTag
                        + " now " + tag);
            }
            fragment.mTag = tag;
        }
        // Fragment 要添加进那个布局
        if (containerViewId != 0) {
            if (containerViewId == View.NO_ID) {
                throw new IllegalArgumentException("Can't add fragment "
                        + fragment + " with tag " + tag + " to container view with no id");
            }
            if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
                throw new IllegalStateException("Can't change container ID of fragment "
                        + fragment + ": was " + fragment.mFragmentId
                        + " now " + containerViewId);
            }
            fragment.mContainerId = fragment.mFragmentId = containerViewId;
        }

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

方法还是比较容易看懂的,就是对要添加的 Fragment 的信息做一些保存,然后调用了 addOp() 方法,接着看。

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

static final class Op {
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;

        Op() {
        }

        Op(int cmd, Fragment fragment) {
            this.cmd = cmd;
            this.fragment = fragment;
        }
    }

方法也是比较简单的,将一个 Op 对象添加进入一个 ArrayList 中,而 Op 中保存了启动这个 Fragment 时需要的属性,以及要对这个 Fragment 要做的事情,也就是 Op 对象 cmd 这个属性,这个属性在 BackStackRecord 中是定义好的,需要添加一个 Fragment 就是使用 OP_ADD ,这个源码中以及写好评,我们不能更改的。
到此,add() 方法所做的事情完结,总结下来 add() 方法所做的事情就是将需要添加 Fragment 的信息进行记录,然后将这些信息和要添加的 Fragment 以 Op 的形式添加进一个 ArrayList 中。下面我们接着看 add() 操作之后的另一个方法 commit()。

4、commit() 方法

依然的看 BackStackRecord 类中的 commit() 方法。

@Override
    public int commit() {
        return commitInternal(false);
    }

int commitInternal(boolean allowStateLoss) {
        if (mCommitted) throw new IllegalStateException("commit already called");
        if (FragmentManagerImpl.DEBUG) {
            Log.v(TAG, "Commit: " + this);
            LogWriter logw = new LogWriter(TAG);
            PrintWriter pw = new PrintWriter(logw);
            dump("  ", null, pw, null);
            pw.close();
        }
        mCommitted = true;
        // 如果需要将这一次 Fragment 事务加入回退栈,就将其加入
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        // 调用 FragmentManager 异步执行事务
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }

commit() 方法直接调用了 commitInternal() ,commitInternal() 调用 FragmentManager 异步执行事务。

public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mDestroyed || mHost == null) {
                if (allowStateLoss) {
                    // This FragmentManager isn't attached, so drop the entire transaction.
                    return;
                }
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<>();
            }
            mPendingActions.add(action);
            scheduleCommit();
        }
    }


interface OpGenerator {
        boolean generateOps(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop);
    }

该方法接收的参数时一个 OpGenerator 而 BackStackRecord 实现了这个接口,OpGenerator 中只有一个方法,然后将我们上一步 add() 的事务添加进入待执行队列 mPendingActions 当中,然后调用 scheduleCommit() 方法开始执行提交。

private void scheduleCommit() {
        synchronized (this) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                // 执行待执行列的事务
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

到此事务执行完毕。
参考:https://xiazdong.github.io/2017/06/15/android-fragment/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值