Android Fragment之事务原理源码分析

194 阅读7分钟

一、整体概述

在 Android 开发中,Fragment 是一种可以嵌入在 Activity 中的 UI 片段,它使得代码更加模块化、可复用,并且能更好地适配不同屏幕尺寸和设备类型。而 Fragment 事务(FragmentTransaction)则是用于管理 Fragment 的添加、移除、替换等操作的关键机制。理解 Fragment 事务的原理对于开发高质量的 Android 应用至关重要。本文将深入探讨 Android Fragment 事务的原理,从基本概念入手,逐步分析其源码实现和工作流程。

二、Fragment 与 Fragment 事务的基本概念

2.1 Fragment 概述

Fragment 是 Android 3.0(API 级别 11)引入的一个组件,它代表了 Activity 中的一个行为或者界面的一部分。可以将多个 Fragment 组合到一个 Activity 中,以创建多面板 UI,并且可以在多个 Activity 中复用一个 Fragment。Fragment 有自己的生命周期,并且可以响应自己的输入事件。

2.2 Fragment 事务的定义

Fragment 事务是一组对 Fragment 的操作集合,例如添加、移除、替换、隐藏、显示等。通过 Fragment 事务,可以在运行时动态地改变 Activity 中的 Fragment 布局。Fragment 事务由 FragmentTransaction 类来表示,它提供了一系列的方法来执行这些操作。

2.3 Fragment 事务的使用场景

  • 动态界面切换:在应用运行时,根据用户的操作或者业务逻辑,动态地添加、移除或替换 Fragment,实现界面的动态变化。
  • 多面板布局:在大屏幕设备上,使用多个 Fragment 组合成多面板布局,提供更好的用户体验。
  • 代码复用:将一些通用的 UI 逻辑封装在 Fragment 中,在不同的 Activity 中复用这些 Fragment。

三、Fragment 事务的基本使用

3.1 获取 FragmentManager

在使用 Fragment 事务之前,需要先获取 FragmentManager。在 Activity 中,可以通过 getSupportFragmentManager()(对于使用 Support Library 的情况)或 getFragmentManager()(对于 API 级别 11 及以上的原生实现)方法来获取。

// 在 Activity 中获取 FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();

3.2 开启 FragmentTransaction

通过 FragmentManager 的 beginTransaction() 方法可以开启一个新的 Fragment 事务。

// 开启一个新的 Fragment 事务
FragmentTransaction transaction = fragmentManager.beginTransaction();

3.3 执行 Fragment 操作

FragmentTransaction 提供了一系列的方法来执行 Fragment 操作,例如 add()remove()replace() 等。

// 创建一个新的 Fragment 实例
MyFragment myFragment = new MyFragment();

// 添加 Fragment 到指定的容器中
transaction.add(R.id.fragment_container, myFragment);

// 替换指定容器中的 Fragment
transaction.replace(R.id.fragment_container, myFragment);

// 移除一个 Fragment
transaction.remove(myFragment);

3.4 提交事务

执行完所有的 Fragment 操作后,需要调用 FragmentTransaction 的 commit() 或 commitNow() 方法来提交事务。

// 提交事务
transaction.commit();

// 立即提交事务
transaction.commitNow();

四、Fragment 事务的源码分析

4.1 FragmentManager 的实现

FragmentManager 是管理 Fragment 和 Fragment 事务的核心类。在 Support Library 中,FragmentManager 的具体实现是 FragmentManagerImpl

// FragmentManagerImpl 类的部分源码
public abstract class FragmentManagerImpl {
    // 存储所有活动的 Fragment
    ArrayList<Fragment> mActive;
    // 存储回退栈中的事务
    ArrayList<BackStackRecord> mBackStack;
    // 存储待执行的事务
    ArrayList<FragmentTransaction> mPendingActions;

    // 开启一个新的 Fragment 事务
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }

    // 执行待执行的事务
    public boolean execPendingActions() {
        // 执行事务的逻辑
    }
}

4.1.1 mActive 列表

mActive 列表用于存储所有活动的 Fragment。当一个 Fragment 被添加到 Activity 中时,会被添加到这个列表中;当 Fragment 被移除时,会从这个列表中移除。

4.1.2 mBackStack 列表

mBackStack 列表用于存储回退栈中的事务。当调用 FragmentTransaction 的 addToBackStack() 方法时,当前事务会被添加到回退栈中。当用户按下返回键时,会从回退栈中弹出最后一个事务并执行其逆向操作。

4.1.3 mPendingActions 列表

mPendingActions 列表用于存储待执行的事务。当调用 FragmentTransaction 的 commit() 方法时,事务会被添加到这个列表中,然后在合适的时机由 execPendingActions() 方法执行。

4.2 FragmentTransaction 的实现

FragmentTransaction 是一个抽象类,其具体实现是 BackStackRecordBackStackRecord 继承自 FragmentTransaction,并实现了所有的 Fragment 操作方法。

// BackStackRecord 类的部分源码
public class BackStackRecord extends FragmentTransaction implements FragmentManager.BackStackEntry,
        FragmentManager.OpGenerator {
    // 操作类型,如 OP_ADD、OP_REMOVE 等
    static final int OP_ADD = 1;
    static final int OP_REMOVE = 2;
    static final int OP_REPLACE = 3;

    // 操作列表
    ArrayList<Op> mOps;
    // 是否添加到回退栈
    boolean mAddToBackStack;

    // 构造函数
    public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
        mOps = new ArrayList<>();
    }

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

    // 执行添加操作
    private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
        Op op = new Op();
        op.cmd = opcmd;
        op.fragment = fragment;
        op.containerViewId = containerViewId;
        mOps.add(op);
        if (tag != null) {
            fragment.mTag = tag;
        }
    }

    // 提交事务
    @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);
        }
        mCommitted = true;
        if (mAddToBackStack) {
            mIndex = mManager.allocBackStackIndex(this);
        } else {
            mIndex = -1;
        }
        mManager.enqueueAction(this, allowStateLoss);
        return mIndex;
    }
}

4.2.1 mOps 列表

mOps 列表用于存储当前事务中的所有操作。每个操作由一个 Op 对象表示,Op 对象包含操作类型(如 OP_ADDOP_REMOVE 等)和要操作的 Fragment。

4.2.2 mAddToBackStack 标志

mAddToBackStack 标志用于指示当前事务是否要添加到回退栈中。如果设置为 true,则当用户按下返回键时,会执行该事务的逆向操作。

4.2.3 commitInternal() 方法

commitInternal() 方法是提交事务的核心方法。它首先检查事务是否已经提交,然后根据 mAddToBackStack 标志分配一个回退栈索引,最后调用 FragmentManager 的 enqueueAction() 方法将事务添加到待执行列表中。

4.3 Fragment 操作的执行流程

当调用 FragmentTransaction 的 commit() 方法后,事务会被添加到 FragmentManager 的 mPendingActions 列表中。FragmentManager 会在合适的时机调用 execPendingActions() 方法来执行这些事务。

// FragmentManagerImpl 的 execPendingActions() 方法的部分源码
public boolean execPendingActions() {
    if (mExecutingActions) {
        return false;
    }
    boolean didSomething = false;
    synchronized (this) {
        if (mPendingActions.size() == 0) {
            return false;
        }
        mExecutingActions = true;
        while (mPendingActions.size() > 0) {
            ArrayList<FragmentTransaction> actions = new ArrayList<>(mPendingActions);
            mPendingActions.clear();
            for (int i = 0; i < actions.size(); i++) {
                FragmentTransaction ft = actions.get(i);
                ft.execute(this);
            }
        }
        mExecutingActions = false;
        didSomething = true;
    }
    moveToState(mCurState, false);
    return didSomething;
}

4.3.1 事务执行的循环

execPendingActions() 方法会遍历 mPendingActions 列表,依次执行每个事务的 execute() 方法。

4.3.2 execute() 方法

execute() 方法会遍历 BackStackRecord 中的 mOps 列表,根据操作类型执行相应的操作。

/ BackStackRecord 的 execute() 方法的部分源码
@Override
public void execute(FragmentManagerImpl manager) {
    boolean popBackStack = false;
    for (int opNum = 0; opNum < mOps.size(); opNum++) {
        Op op = mOps.get(opNum);
        switch (op.cmd) {
            case OP_ADD: {
                Fragment f = op.fragment;
                f.mNextAnim = op.enterAnim;
                manager.addFragment(f, false);
                break;
            }
            case OP_REMOVE: {
                Fragment f = op.fragment;
                f.mNextAnim = op.exitAnim;
                manager.removeFragment(f, mTransition, mTransitionStyle);
                break;
            }
            case OP_REPLACE: {
                int containerViewId = op.containerViewId;
                Fragment f = op.fragment;
                f.mNextAnim = op.enterAnim;
                Fragment old = manager.findFragmentById(containerViewId);
                if (old != null) {
                    old.mNextAnim = op.exitAnim;
                    manager.removeFragment(old, mTransition, mTransitionStyle);
                }
                if (f != null) {
                    manager.addFragment(f, false);
                }
                break;
            }
        }
    }
    if (mAddToBackStack) {
        manager.addBackStackState(this);
    }
}

4.3.3 操作类型的处理

在 execute() 方法中,根据 Op 对象的 cmd 字段的值,执行不同的操作。例如,当 cmd 为 OP_ADD 时,调用 FragmentManager 的 addFragment() 方法添加 Fragment;当 cmd 为 OP_REMOVE 时,调用 FragmentManager 的 removeFragment() 方法移除 Fragment。

五、Fragment 事务的生命周期管理

5.1 Fragment 的生命周期

Fragment 有自己独立的生命周期,与 Activity 的生命周期相互关联。Fragment 的生命周期方法包括 onAttach()onCreate()onCreateView()onViewCreated()onActivityCreated()onStart()onResume()onPause()onStop()onDestroyView()onDestroy() 和 onDetach()

5.2 Fragment 事务对生命周期的影响

不同的 Fragment 操作会影响 Fragment 的生命周期。例如,当调用 add() 方法添加一个 Fragment 时,会依次调用 Fragment 的 onAttach()onCreate()onCreateView()onViewCreated()onActivityCreated()onStart() 和 onResume() 方法;当调用 remove() 方法移除一个 Fragment 时,会依次调用 Fragment 的 onPause()onStop()onDestroyView()onDestroy() 和 onDetach() 方法。

5.3 源码分析:Fragment 生命周期的调用

在 FragmentManager 的 addFragment() 和 removeFragment() 等方法中,会根据操作类型调用相应的 Fragment 生命周期方法。

// FragmentManagerImpl 的 addFragment() 方法的部分源码
public void addFragment(Fragment fragment, boolean moveToStateNow) {
    if (mActive == null) {
        mActive = new ArrayList<>();
    }
    if (mAdded == null) {
        mAdded = new ArrayList<>();
    }
    if (DEBUG) Log.v(TAG, "add: " + fragment);
    makeActive(fragment);
    if (!fragment.mDetached) {
        if (mAdded.contains(fragment)) {
            throw new IllegalStateException("Fragment already added: " + fragment);
        }
        mAdded.add(fragment);
        fragment.mAdded = true;
        fragment.mRemoving = false;
        if (fragment.mHasMenu && fragment.mMenuVisible) {
            mNeedMenuInvalidate = true;
        }
        if (moveToStateNow) {
            moveToState(fragment, mCurState, 0, 0, false);
        }
    }
}

// FragmentManagerImpl 的 moveToState() 方法的部分源码
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
    if (f.mState <= newState) {
        switch (f.mState) {
            case Fragment.INITIALIZING:
                if (DEBUG) Log.v(TAG, "moveto CREATED: " + f);
                f.mCalled = false;
                f.onAttach(mHost.getContext());
                if (!f.mCalled) {
                    throw new SuperNotCalledException("Fragment " + f
                            + " did not call through to super.onAttach()");
                }
                if (mHost.getFragmentManager() != null) {
                    f.mFragmentManager = mHost.getFragmentManager();
                }
                f.mState = Fragment.CREATED;
                // 其他生命周期方法的调用
                break;
            case Fragment.CREATED:
                if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                f.mCalled = false;
                f.onActivityCreated(f.mSavedFragmentState);
                if (!f.mCalled) {
                    throw new SuperNotCalledException("Fragment " + f
                            + " did not call through to super.onActivityCreated()");
                }
                f.mState = Fragment.ACTIVITY_CREATED;
                // 其他生命周期方法的调用
                break;
        }
    } else if (f.mState > newState) {
        // 处理 Fragment 状态下降的情况
    }
}

5.3.1 addFragment() 方法

addFragment() 方法会将 Fragment 添加到 mActive 和 mAdded 列表中,并根据 moveToStateNow 参数决定是否立即调用 moveToState() 方法来更新 Fragment 的状态。

5.3.2 moveToState() 方法

moveToState() 方法是管理 Fragment 生命周期的核心方法。它会根据 Fragment 的当前状态和目标状态,调用相应的生命周期方法。例如,当 Fragment 的状态从 INITIALIZING 变为 CREATED 时,会调用 onAttach() 方法。

六、Fragment 事务的回退栈管理

6.1 回退栈的概念

回退栈是一个栈结构,用于存储 Fragment 事务。当调用 FragmentTransaction 的 addToBackStack() 方法时,当前事务会被添加到回退栈中。当用户按下返回键时,会从回退栈中弹出最后一个事务并执行其逆向操作。

6.2 回退栈的使用

// 创建一个新的 Fragment 事务
FragmentTransaction transaction = fragmentManager.beginTransaction();

// 添加一个 Fragment 并将事务添加到回退栈中
MyFragment myFragment = new MyFragment();
transaction.add(R.id.fragment_container, myFragment);
transaction.addToBackStack(null);
transaction.commit();