Android Fragment之FragmentManager 源码分析

一、FragmentManager 的基本概念与作用

在深入源代码之前,先简要回顾一下 FragmentManager 的基本概念。FragmentManager 负责管理 Fragment 的全生命周期,包括创建、添加、移除、替换以及处理 Fragment 之间的交互等操作。它是 Activity 与 Fragment 之间沟通的桥梁,通过它,Activity 能够对 Fragment 进行精准控制,从而构建出复杂多变的用户界面。

二、FragmentManager 的核心类结构

2.1 FragmentManager 抽象类

FragmentManager 是一个抽象类,它定义了一系列用于管理 Fragment 的关键方法,为开发者提供了操作 Fragment 的统一接口。其部分关键方法如下:

public abstract class FragmentManager {
    // 开启一个Fragment事务
    public abstract FragmentTransaction beginTransaction();
    // 根据资源ID查找对应的Fragment
    public abstract Fragment findFragmentById(int id);
    // 根据标签查找对应的Fragment
    public abstract Fragment findFragmentByTag(String tag);
    // 从回退栈中弹出事务
    public abstract void popBackStack();
    // 更灵活的回退栈操作
    public abstract void popBackStack(String name, int flags);
    // 添加回退栈变化监听器
    public abstract void addOnBackStackChangedListener(OnBackStackChangedListener listener);
}

这些方法构成了开发者与 FragmentManager 交互的基础,通过它们,我们能够轻松地对 Fragment 进行各种管理操作。然而,FragmentManager 抽象类本身并不实现具体的功能,真正的实现逻辑位于其具体的实现类中。

2.2 FragmentManagerImpl 实现类

FragmentManagerImpl 是 FragmentManager 抽象类的具体实现类,承担了大部分实际的管理工作。它内部维护了多个重要的数据结构和状态信息,以实现对 Fragment 的高效管理。

2.2.1 重要数据结构
class FragmentManagerImpl extends FragmentManager {
    // 存储当前处于活动状态的Fragment列表
    final ArrayList<Fragment> mActive = new ArrayList<>();
    // 用于记录Fragment事务回退栈
    final Stack<BackStackRecord> mBackStack = new Stack<>();
    // 用于创建Fragment实例的工厂
    FragmentFactory mFragmentFactory;
}
  • mActive 列表:这个列表存储了当前所有处于活动状态的 Fragment。FragmentManagerImpl 通过维护这个列表,能够快速地访问和管理这些 Fragment,例如在 Activity 生命周期发生变化时,遍历该列表并调用相应 Fragment 的生命周期方法。
  • mBackStack 栈:它记录了 Fragment 事务的回退信息。当一个 Fragment 事务被添加到回退栈时,相关的操作记录会被压入这个栈中。当用户执行返回操作时,FragmentManagerImpl 会从栈中弹出记录,并根据记录恢复之前的 Fragment 状态,从而实现类似浏览器回退的功能。
  • mFragmentFactory:这是一个用于创建 Fragment 实例的工厂。通过使用工厂模式,FragmentManagerImpl 提供了一种可扩展的方式来创建 Fragment,开发者可以自定义 FragmentFactory,以实现对 Fragment 创建过程的定制。
2.2.2 事务管理方法

FragmentManagerImpl 中的事务管理方法是其核心功能之一。其中,beginTransaction 方法用于开启一个 Fragment 事务,它返回一个 BackStackRecord 对象,这个对象负责记录事务中的具体操作。

@Override
public FragmentTransaction beginTransaction() {
    return new BackStackRecord(this);
}
public class BackStackRecord extends FragmentTransaction {
    @Override
    public FragmentTransaction add(int containerViewId, Fragment fragment) {
        doAddOp(OP_ADD, containerViewId, fragment, null, 0);
        return this;
    }
    private void doAddOp(int cmd, int containerViewId, Fragment fragment, String tag, int opIndex) {
        Op op = new Op();
        op.cmd = cmd;
        op.fragment = fragment;
        op.tag = tag;
        op.containerViewId = containerViewId;
        mOps.add(op);
    }
}

在 add 方法中,首先创建一个 Op 对象,用于记录本次操作的类型(这里是添加操作)、目标 Fragment、容器视图 ID 等信息,然后将这个 Op 对象添加到 mOps 列表中。mOps 列表存储了当前事务中的所有操作记录,当事务提交时,这些操作将按照顺序依次执行。

2.2.3 Fragment 状态管理方法

FragmentManagerImpl 还负责管理 Fragment 的生命周期状态。其中,moveToState 方法是状态管理的核心方法之一,它用于将 Fragment 移动到指定的状态,并触发相应的生命周期回调。

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
    // 根据新状态调用相应的生命周期方法
    if (newState == Fragment.CREATED) {
        f.performCreate(f.mSavedFragmentState);
    } else if (newState == Fragment.STARTED) {
        f.performStart();
    } else if (newState == Fragment.RESUMED) {
        f.performResume();
    }
    // 其他状态处理逻辑...
}

在这个方法中,根据传入的新状态 newState,调用 Fragment 的相应生命周期方法,例如当新状态为 Fragment.CREATED 时,调用 f.performCreate(f.mSavedFragmentState) 方法,触发 Fragment 的 onCreate 回调,从而完成 Fragment 的创建过程。通过这种方式,FragmentManagerImpl 能够精确地控制 Fragment 的状态转换,确保其生命周期的正确执行。

三、Fragment 事务处理流程

3.1 事务创建与构建

当开发者调用 FragmentManager 的 beginTransaction 方法时,实际上是创建了一个 BackStackRecord 对象,这个对象用于记录事务中的一系列操作。例如,下面的代码展示了如何创建一个简单的 Fragment 事务,将一个 Fragment 添加到容器中:

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
MyFragment myFragment = new MyFragment();
transaction.add(R.id.fragment_container, myFragment);

在这段代码中,首先通过 getSupportFragmentManager().beginTransaction() 获取一个 FragmentTransaction 对象,实际上这个对象是 BackStackRecord 类型。然后,创建一个 MyFragment 实例,并调用 transaction.add(R.id.fragment_container, myFragment) 将其添加到指定的容器视图 R.id.fragment_container 中。此时,BackStackRecord 的 mOps 列表中会添加一条记录,记录了本次添加操作的相关信息。

3.2 事务提交

当事务中的所有操作都构建完成后,开发者需要调用 commit 方法来提交事务。commit 方法最终会调用 BackStackRecord 的 commitInternal 方法:

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;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}

在 commitInternal 方法中,首先检查事务是否已经提交,如果已经提交则抛出异常。然后,根据是否将事务添加到回退栈(由 mAddToBackStack 标志决定),进行相应的处理。最后,通过 mManager.enqueueAction(this, allowStateLoss) 将事务添加到 FragmentManagerImpl 的事务队列中等待执行。

3.3 事务执行

FragmentManagerImpl 在合适的时机(通常是在 Activity 的消息循环中)会从事务队列中取出事务并执行。执行事务的过程实际上是遍历 BackStackRecord 的 mOps 列表,依次执行每个操作。例如,对于添加操作,会调用 FragmentManagerImpl 的 addFragment 方法:

void addFragment(Fragment fragment) {
    mActive.add(fragment);
    moveToState(fragment, Fragment.CREATED, 0, 0, true);
}

四、Fragment 生命周期管理

4.1 生命周期状态定义

Fragment 具有多种生命周期状态,这些状态定义在 Fragment 类中:

public class Fragment {
    // Fragment的各种状态
    public static final int INITIALIZING = 0;
    public static final int CREATED = 1;
    public static final int ACTIVITY_CREATED = 2;
    public static final int STARTED = 3;
    public static final int RESUMED = 4;
    public static final int PAUSED = 5;
    public static final int STOPPED = 6;
    public static final int DESTROYED = 7;
    public static final int DETACHED = 8;
    int mState = INITIALIZING;
}

这些状态反映了 Fragment 在不同阶段的运行情况,FragmentManagerImpl 通过管理这些状态来控制 Fragment 的生命周期。

4.2 生命周期状态转换

FragmentManagerImpl 中的 moveToState 方法负责 Fragment 生命周期状态的转换。例如,当 Activity 进入 onStart 生命周期时,FragmentManagerImpl 会遍历 mActive 列表,调用每个 Fragment 的 moveToState 方法,将其状态转换为 STARTED

void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) {
    // 状态转换逻辑
    if (f.mState < newState) {
        switch (newState) {
            case Fragment.STARTED:
                f.performStart();
                break;
            // 其他状态处理
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.STARTED:
                f.performStop();
                break;
            // 其他状态处理
        }
    }
    f.mState = newState;
}

在这个方法中,首先比较 Fragment 当前状态 f.mState 和目标状态 newState。如果当前状态小于目标状态,则根据目标状态调用相应的生命周期方法,如目标状态为 STARTED 时,调用 f.performStart() 方法,触发 Fragment 的 onStart 回调。如果当前状态大于目标状态,则调用相应的反向生命周期方法,如当前状态为 STARTED,目标状态为 STOPPED 时,调用 f.performStop() 方法,触发 Fragment 的 onStop 回调。最后,更新 Fragment 的状态为目标状态 newState。通过这种方式,FragmentManagerImpl 能够确保 Fragment 的生命周期状态与 Activity 的生命周期以及用户操作保持一致,实现了精确的生命周期管理。

### FragmentManager 使用方法 FragmentManager 是用于管理和操作应用中的 `Fragment` 的类。通过该组件可以执行诸如添加、移除以及替换片段的操作。 #### 启用调试日志 为了便于开发期间跟踪 `FragmentManager` 执行的动作,可启用其内部的日志记录功能: ```java FragmentManager.enableDebugLogging(true); ``` 此设置有助于开发者监控事务的变化过程并排查潜在问题[^1]。 #### 添加Fragment实例至Activity 要向 Activity 中加入一个新的 Fragment 实例,需先获取当前上下文中可用的 `SupportFragmentManager` 对象,接着启动一个 `FragmentTransaction` 来定义待实施的一系列变更动作,最后调用 `commit()` 方法提交这些更改: ```java FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(R.id.fragment_container, new ExampleFragment()); transaction.commit(); ``` 上述代码展示了如何创建新的 `ExampleFragment` 并将其放置于由 ID `R.id.fragment_container` 标识的容器内[^2]。 #### 替换现有Fragment 当需要更新界面上显示的内容时,可通过 `replace()` 函数来实现旧有 fragment 到新fragment之间的转换: ```java FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.replace(R.id.fragment_container, new AnotherFragment()); transaction.addToBackStack(null); // 可选:允许用户返回上一状态 transaction.commit(); ``` 这里不仅替换了指定位置上的内容还提供了回退机制以便恢复之前的视图层次结构。 #### 常见问题及其解决方案 - **无法找到FragmentContainerView**: 如果遇到此类错误提示,则可能是由于布局文件里缺少相应的容器控件或是ID不匹配所引起的。应仔细核对 XML 文件内的声明部分确保无误。 - **非法状态异常 (IllegalStateException)**: 当尝试在一个已经结束的生命週期内发起事务请求时会发生这种情况。建议在适当的地方(比如生命周期回调函数)处理所有的 UI 更新逻辑,并考虑使用 `isFinishing()` 或者 `isDestroyed()` 进行额外的状态验证以防止意外发生。 - **重复提交相同的事务**: 若发现多次点击按钮触发相同事件却只生效一次的现象,这可能是因为每次交互都试图重新提交同一笔未完成的任务所致。对于这类情形可以在提交之前检查是否有正在进行中的事务存在(`beginTransaction().isNotCommitted`),或者利用 `commitAllowingStateLoss()` 跳过某些不必要的同步等待阶段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android 小码蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值