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

本文章分析了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方法是核心处理逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/591521.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

介绍BCD码

BCD码&#xff08;Binary-Coded Decimal‎&#xff09;&#xff0c;用4位 二进制数 来表示1位 十进制数 中的0~9这10个数码&#xff0c;是一种二进制的数字编码形式&#xff0c;用 二进制编码的十进制 代码。 BCD码这种编码形式利用了四个位元来储存一个十进制的数码&#xff0…

关于莫比乌斯变换属性梳理

文章目录 一、说明二、多视角看莫比乌斯变换2.1 从几何角度2.2 复分析中的莫比乌斯变换2.3 莫比乌斯变换运算组合2.4 莫比乌斯变换的不动点2.5 三个点决定一个莫比乌斯变换2.6 交叉比2.7 莫比乌斯变换的逆变换 三 莫比乌斯变换性质证明3.1 证明1&#xff1a;莫比乌斯变换将圆变…

最新SpringBoot项目地方废物回收机构管理系统

采用技术 最新SpringBoot项目地方废物回收机构管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 登录页面 后端管理员 管理员首页 员工管理 设…

微星主板安装双系统不能进入Ubuntu的解决办法

在微星主板的台式机上面依次安装了Windows11和Ubuntu22.04。在Ubuntu安装完成后重启&#xff0c;没有出现系统选择界面&#xff0c;直接进入了Windows11。怎么解决&#xff1f;方法如下&#xff1a; &#xff08;1&#xff09;正常安装Windows11 &#xff08;2&#xff09;安…

基于php+mysql+html简单图书管理系统

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、Php、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

需求规格说明书编制书(word原件)

1 范围 1.1 系统概述 1.2 文档概述 1.3 术语及缩略语 2 引用文档 3 需求 3.1 要求的状态和方式 3.2 系统能力需求 3.3 系统外部接口需求 3.3.1 管理接口 3.3.2 业务接口 3.4 系统内部接口需求 3.5 系统内部数据需求 3.6 适应性需求 3.7 安全性需求 3.8 保密性需…

# IDEA 复制项目 Module 出现 不同模块下的 Product 类报错

IDEA 复制项目 Module 出现 不同模块下的 Product 类报错 我们 用 IDEA 复制项目 Module 出现 不同模块下的 Product 类报错&#xff0c;发现复制的 module 名称没有改变或者 java 文件夹后面还有原项目 source root 字样&#xff0c;maven 父子项目没有标识等问题。 解决方法…

二叉树四种遍历方法

目录 树基本概念 二叉树二叉树的五种形态特殊二叉树二叉链表创建四种遍历方法代码实现 树 树是一个n个节点的有限集&#xff0c;当n0时称之为空树 基本概念 性质 1. 树的定义是递归的&#xff0c;树的定义中又用到了自身 2. 树的根节点没有前驱&#xff0c;除根结点外&#x…

开了个新店!

大家好&#xff0c;我是麦鸽。 一言难尽&#xff0c;五一之前&#xff0c;把大A里的钱都提出来了&#xff0c;又整了一个新的小店。熟悉我的老读者应该都知道&#xff0c;我主业是做嵌入式的&#xff0c;后面慢慢转了技术管理的路线。平时也搞点副业&#xff0c;餐饮店就是其中…

关于链表带环问题为什么要用快慢指针

判断链表是否带环 题目描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连…

定制开发AI名片商城AI导购系统:引领营销自动化的新时代

在数字营销日新月异的今天&#xff0c;一个革命性的工具——定制开发AI名片商城AI导购系统&#xff0c;正逐渐崭露头角&#xff0c;成为企业私域运营中的得力助手。它不仅仅是一个营销工具&#xff0c;更是一个拥有强大营销自动化能力和先进算法技术的在线助理&#xff0c;为企…

Ubuntu 域名解析出现暂时性错误

Ubuntu 域名解析出现暂时性错误 问题描述解决方案 问题描述 由于在Ubuntu系统里面经常切换网络导致&#xff0c;系统一直处于有线网络连接但是没网状态&#xff0c;尝试ping网络也无法完成&#xff0c;尝试了很多方法均不能解决 解决方案 点击”虚拟机“ 按照要求设置好即可…

Java | Spring框架 | BeanFactory与ApplicationContext

Spring容器&#xff1a;BeanFactory与ApplicationContext Spring容器是Spring框架的核心&#xff0c;负责实例化、配置和组装Bean。 Spring容器有两种主要类型&#xff1a;BeanFactory和ApplicationContext。 一、BeanFactory 基本功能&#xff1a;BeanFactory是Spring框架…

【数据库原理及应用】期末复习汇总高校期末真题试卷02

试卷 一、填空题 数据库系统是指计算机系统中引入数据库后的系统&#xff0c;一般由数据库、________、应用系统、数据库管理员和用户构成。当数据库的存储结构发生了改变&#xff0c;由数据库管理员对________映象作相应改变&#xff0c;可以使________保持不变&#xff0c;…

牛客热题:两个链表的第一个公共节点

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;两个链表的第一个公共节点题目链…

目标检测算法YOLOv5简介

没有关于YOLOv5的直接论文&#xff0c;YOLOv5由Ultralytics维护&#xff0c;源码见&#xff1a;https://github.com/ultralytics/yolov5 &#xff0c;于2020年6月发布v1.0版本&#xff0c;最新发布版本为v7.0&#xff0c;License为AGPL-3.0. 以下内容主要来自&#xff1a; 1. U…

STM32的TIM输入捕获和PWMI详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. IC输入捕获 2. 频率测量 3. 主模式、从模式、触发源选择 4. 输入捕获基本结构 5. PWMI模式 6. 代码示例 6.1 PWM.c 6.2 PWM.h 6.3 IC.c 6.4 IC.h 6.5 完整工程文件 输出比较可以看下面这篇…

ORAN C平面优化

使用section扩展6的C平面优化 在时域和频域中&#xff0c;都可以使用section扩展6进行非连续PRB分配。Section扩展6有两个位掩码&#xff1a;symbolMask和rbgMask。使用symbolMask可以选择一个slot内任意的symbol子集。使用rbgMask可以选择startPrbc和&#xff08;startPrbc …

Android版本依赖Version catalog

曾经我们使用config.gradle文件进行版本依赖配置&#xff0c;然后在project的build.gradle.kts中使用如下方式引入&#xff1a; apply(from "./config.gradle") 缺点&#xff1a;在project的module中引用无任何提示&#xff0c;无法跳转到指定引用 一、创建versio…

Go-变量

可以理解为一个昵称 以后这个昵称就代指这些信息 var sg string "czy" 声明赋值 package mainimport "fmt"func main() {var sg string "陈政洋"fmt.Println(sg)var age int 73fmt.Println(age)var flag bool truefmt.Println(flag) } …