展开说说:Android Fragment完全解析-卷三

时间:2024-05-05 07:08:45

本文章分析了Fragment的管理器FragmentManager、事务FragmentTransaction 以及完整的声明周期和动态加载Fragment的原理解析。

1、Fragment管理器

FragmentManager 类负责在应用的 fragment 上执行一些操作,如添加、移除或替换操作,以及将操作添加到返回堆栈。

您可以从 activity 或 fragment 访问 FragmentManager。

FragmentActivity 及其子类(如 AppCompatActivity)都可以过 getSupportFragmentManager() 方法访问 FragmentManager。

fragment 可以托管一个或多个子 fragment。在 fragment 内,您可以通过 getChildFragmentManager() 获取对管理 fragment 子级的 FragmentManager 的引用。如果您需要访问其宿主 FragmentManager,可以使用 getParentFragmentManager()。

如需在布局容器中显示 fragment,请使用 FragmentManager 创建 FragmentTransaction。在事务中,您随后可以对容器执行 add() 或 replace() 操作。您可以使用 findFragmentById() 获取对布局容器中当前 fragment 的引用。这些都是我们动态创建并使用fragment的常规操作。

这里说一句哈,FragmentManager是个抽象类因此它的实际工作由子类FragmentManagerImpl帮忙完成。

2、Fragment事务

上面提到了,FragmentManager 可以通过 Fragment 执行添加、移除、替换以及其他操作,以响应用户互动。但实际replace替换、add添加、hide隐藏、show显示、remove移除等方法都不是FragmentManager的而是FragmentTransaction 类提供。您提交的每组 Fragment 都称为一个“事务”,可以使用 FragmentTransaction 类提供的上述 API 指定在事务内需执行何种操作。每个 事务必须执行提交。commit() 调用会向 FragmentManager 发出信号,指明所有操作均已添加到事务中。

您可以将多个操作分组到一个事务中。例如,通过一个事务就可以添加或替换多个 Fragment。当您在同一个屏幕上显示多个同级 Fragment(例如使用分块视图)时,该分组会很实用。您也可将每个事务保存到由 FragmentManager 管理的返回堆栈内,从而让用户能够回退 Fragment 更改(类似于回退 Activity)。

调用 beginTransaction() 从 FragmentManager 获取 FragmentTransaction 实例。

3、生命周期

这是一张来自官网的Fragment生命周期流程图。其实这里少了最初始onAttach() 和 最后的onDetach()回调方法,我们加上他俩一起来看。

每个 Fragment 显示或被销毁都有自己的生命周期。当用户浏览应用并与之互动时,您的 Fragment 会在添加、移除时以及进入或退出屏幕时完成其生命周期内各种状态的转换。下面我们按着一个Fragment展示到被销毁的过程分析13个生命周期:

(1)将 Fragment 添加到 FragmentManager 并附加到其宿主 Activity 后,系统将调用 onAttach() 回调,最早的。

 (2)Fragment 的 SavedStateRegistry 恢复与 Fragment 本身关联的所有已保存状态,会调用 onCreate() 回调。此时尚未创建 Fragment 的视图,只有在创建该视图后,才应恢复与该 Fragment 的视图关联的任何状态。

(3)当 Fragment 提供有效的 View 实例时,才会创建 Fragment 的视图 Lifecycle,这会在适当的时间自动膨胀视图。系统将调用 onCreateView()回调。这方法也是我们最常用的回调之一,重写他提供对应的布局view对象创建视图。

(4)当且仅当 Fragment 的视图已使用非 null View 实例化后,该 View 才会在 Fragment 上设置,才能使用 getView() 检索到,系统调用 onViewCreated() 回调。

创建 Fragment 的视图后,系统会恢复之前的视图状态(如有),系统调

(5)用 onViewStateRestored() 回调。如果 Fragment 的视图为非 null,在 Fragment 的 Lifecycle 转为 STARTED 后,Fragment 的视图 Lifecycle 会立即转为 STARTED。当 Fragment 转为 STARTED 时,系统会调用 onStart() 回调。

(6)如果 Fragment 可见,即表示所有 Animator 和 Transition 效果均已完成,且 Fragment 已做好与用户互动的准备。该 Fragment 的 Lifecycle 会转为 RESUMED 状态,并且系统会调用 onResume() 回调。

(7)当用户开始离开 Fragment,但是 Fragment 仍然可见时,Fragment 及其视图的 Lifecycle 会返回 STARTED 状态,并向其观察者发出 ON_PAUSE 事件。系统会调用其 onPause() 回调。

(8)Fragment 不再可见后,Fragment 及其视图的 Lifecycle 将转为 CREATED 状态,并向其观察者发出 ON_STOP 事件。系统会调用其 onStop() 回调。

(9)完成所有退出动画和转换并且 Fragment 的视图与窗口分离之后,Fragment 的视图 Lifecycle 会转为 DESTROYED 状态并向其观察者发出 ON_DESTROY 事件。然后,Fragment 会调用其 onDestroyView() 回调。此时,Fragment 的视图的生命周期结束

(10)如果 Fragment 已被移除,或者 FragmentManager 已被销毁,Fragment 的 Lifecycle 会转为 DESTROYED 状态,并向其观察者发送 ON_DESTROY 事件。然后,Fragment 会调用其 onDestroy() 回调。此时,Fragment 的生命周期结束。

(11)将 Fragment 从 FragmentManager 中移除并将其与其宿主 Activity 分离后,系统会调用 onDetach() 回调。该 Fragment 不再处于活跃状态,无法再使用 findFragmentById() 检索到。

首先通过源码可以知道,Fragment类 会实现 LifecycleOwner,LifecycleOwner内部有一个方法Lifecycle getLifecycle(),返回的是一个Lifecycle 对象;

Lifecycle 是一个抽象类生命周期状态状态均在 Lifecycle.State 枚举中表示。

/**
 * Lifecycle states. You can consider the states as the nodes in a graph and
 * {@link Event}s as the edges between these nodes.
 */
@SuppressWarnings("WeakerAccess")
public enum State {
    /**
     * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
     * any more events. For instance, for an {@link android.app.Activity}, this state is reached
     * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
     */
    DESTROYED,

    /**
     * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
     * the state when it is constructed but has not received
     * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
     */
    INITIALIZED,

    /**
     * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached in two cases:
     * <ul>
     *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
     *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
     * </ul>
     */
    CREATED,

    /**
     * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached in two cases:
     * <ul>
     *     <li>after {@link android.app.Activity#onStart() onStart} call;
     *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
     * </ul>
     */
    STARTED,

    /**
     * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
     * is reached after {@link android.app.Activity#onResume() onResume} is called.
     */
    RESUMED;

    /**
     * Compares if this State is greater or equal to the given {@code state}.
     *
     * @param state State to compare with
     * @return true if this State is greater or equal to the given {@code state}
     */
    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
}

顺着我们重写的声明周期方法向上追,就是下面的顺序:先进入Fragment类中onCreate方法 然后是被 performxx方法调用,这些个performxx都是被FragmentManagerImpl的moveToState方法区分了状态来调用的。以下是以onCreate 和onResume 为例,其他生命周期方法也是一样。

Fragment类中onCreate - performCreate - FragmentManagerImpl的moveToState

Fragment类中onResume - performResume - FragmentManagerImpl的moveToState

Fragment类中定义了这些常量和一个变量mState ,mState 就是记录这些常量的一个变量值。最后都是把上面枚举值转变为这个这些常量用mState 再进行调用我们熟知的生命周期方法。

static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // Fully created, not started.
static final int STARTED = 3;          // Created and started, not resumed.
static final int RESUMED = 4;          // Created started and resumed.

int mState = INITIALIZING;

FragmentManagerImplmoveToState方法非常重要,但是代码行数很多,这贴出一部分图您就大致明了了。截图部分可以看到有两个分支分别调用了performPause()和performStop()

  1. 动态加载Fragment的原理解析

add和replaca方法:

FragmentTransaction类的 - add - doAddOp - addOp -
FragmentTransactiond类的 - replace - doAddOp - addOp -

两个方法执行路线极为相似,只在doAddOp方法传入里两个不同的值OP_ADD和OP_REPLACE,也就决定了后面的处理一定会依赖这两个值。

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

//======

@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
        @Nullable String tag)  {
    if (containerViewId == 0) {
        throw new IllegalArgumentException("Must use non-zero containerViewId");
    }
    doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    return this;
}

上面提到过操作fragment必须执行提交操作,否则无效。所以commit才是关键。


调用commit方法:

FragmentTransaction也是个抽象类,它的子类实现类是BackStackRecord。


BackStackRecord类 - commit - commitInternal - 调用FragmentManagerImpl类 - enqueueAction  -scheduleCommit - updateOnBackPressedCallbackEnabled - tmHost.getHandler().post(mExecCommit)将任务发到run方法执行 - execPendingActions - removeRedundantOperationsAndExecute - executeOpsTogether - 调用BackStackRecord类 - expandOps, expandOps方法是核心处理逻辑。