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

news2025/1/15 12:56:04

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建MongoDB分片集群

文章目录 一、什么是分片二、分片集群1、组件构成2、分片集群内各组件间交互 三、数据如何切分四、分片策略1、哈希分片2、范围分片 五、分片集群架构六、搭建分片集群1、涉及主机2、所有主机安装MongoDB3、分片节点副本集的创建3.1、第一套副本集shard13.1.1、准备存放数据和日…

python项目==一个web项目,配置模板指定文件清洗规则,调用模板规则清洗文件

代码地址 一个小工具。 一个web项目&#xff0c;配置模板指定文件清洗规则&#xff0c;调用模板规则清洗文件 https://github.com/hebian1994/csv-transfer-all 技术栈&#xff1a; SQLite python flask vue3 elementplus 功能介绍&#xff1a; A WEB tool for cleaning…

Pyside6详细使用教程python之GUI开发

1、首先需要安装Pyside6&#xff0c;终端执行命令&#xff1a; pip3.10 install pyside6 2、你们的一般是 pip install pyside6 2、如下代码创建一个简易程序导入必要的模块 import sys from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton,…

three.js入门指南

WebGL和Three.js的概念 什么是WebGL WebGL是基于OpenGL ES 2.0的Web标准&#xff0c;可以通过HTML5 Canvas元素作为DOM接口访问。 也就是WebGL是作为OpenGL的网页端入口。它作为一个底层标准&#xff0c;然后我们可以通过JavaScript代码&#xff0c;在网页上实现三维图形的渲…

重看Spring聚焦BeanDefinition分析和构造

目录 一、对BeanDefinition的理解 &#xff08;一&#xff09;理解元信息 &#xff08;二&#xff09;BeanDefinition理解分析 二、BeanDefinition的结构设计分析 &#xff08;一&#xff09;整体结构体会 &#xff08;二&#xff09;重要接口和类分析 三、构造 BeanDef…

JavaSE——正则表达式(1/2):概述、初步使用(普通方法,正则表达式)、书写规则(字符类,预定义字符,数量词,其他,特殊案例)

目录 概述 初步使用 普通方法 正则表达式 书写规则 字符类 预定义字符 数量词 其他 特殊案例 概述 正则表达式 就是由一些特定的字符组成&#xff0c;代表的是一个规则。 作用一&#xff1a;用来校验数据格式是否合法 &#xff08;更简单、更便捷&#xff09; 作…

C语言 | Leetcode C语言题解之第66题加一

题目&#xff1a; 题解&#xff1a; /*** Note: The returned array must be malloced, assume caller calls free().*/ int* plusOne(int* digits, int digitsSize, int* returnSize){for(int i digitsSize - 1; i > 0; --i){digits[i] digits[i] 1;//最后元素1判断是不…

SQL注入漏洞扫描---sqlmap

what SQLMap是一款先进的自动执行SQL注入的审计工具。当给定一个URL时&#xff0c;SQLMap会执行以下操作&#xff1a; 判断可注入的参数。判断可以用哪种SQL注入技术来注入。识别出目标使用哪种数据库。根据用户的选择&#xff0c;读取哪些数据库中的数据。 更详细语法请参考…

NIO和NIO.2对比

Java NIO (New Input/Output) 是从Java 1.4版本开始引入的一个新的I/O API&#xff0c;用于替代原来的BIO&#xff08;Blocking I/O&#xff09;API。NIO提供了更加灵活和高效的网络通信方式&#xff0c;特别适合于高吞吐量的网络编程。NIO的主要特点是非阻塞模式&#xff0c;它…

Spark Stream

一、Spark Streaming是什么 Spark Streaming 用于流式数据的处理。Spark Streaming 支持的数据输入源很多&#xff0c;例如&#xff1a;Kafka、Flume、Twitter、ZeroMQ 和简单的 TCP 套接字等等。数据输入后可以用 Spark 的高度抽象原语如&#xff1a;map、reduce、join、wind…

linux 服务器利用阿里网盘API实现文件的上传和下载

文章目录 背景脚本初始化 阿里云盘API工具 aligo安装aligoaligo教程实战parse.py 演示上传文件上传文件夹下载文件下载文件夹 背景 最近在用ubuntu系统做实验&#xff0c;而ubuntu 系统的文件上传和下载操作很麻烦&#xff1b; 于是便打算使用阿里网盘的API 进行文件下载与上传…

深度学习每周学习总结P7(咖啡豆识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 –来自百度网盘超级会员V5的分享 数据链接 提取码&#xff1a;7zt2 –来自百度网盘超级会员V5的分享 目录 0. 总结1. 数据导入及处理部分…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-10.1-NXP SDK 移植

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

IDEA 多模块项目报错 Cannot Save Settings 问题

IDEA 多模块项目报错 Cannot Save Settings 问题 Cannot Save Settings&#xff1a; Module "spring_cloud_sentinel_demo" must not contain source root "D:\java_test\Intesij_idea\spring_cloud_sentinel_demo\order_service_rest\src\main\resources"…

一键去除衣物DeepNode合成软件下载

dn软件Windows版下载地址&#xff1a;点击下载 ai一键去除衣物(DeepNode)是一款非常好用的一键换装软件&#xff0c;它可以创造出不同的图像效果&#xff0c;还可以对人的面部特征进行微调&#xff0c;使用户的图片更有特色。软件中还有许多模板可以随意使用以供参考&#xff0…

高效时间序列分析的开源利器:QuestDB

QuestDB&#xff1a;探索数据的深度&#xff0c;加速决策的速度- 精选真开源&#xff0c;释放新价值。 概览 时序数据库&#xff08;Time Series Database&#xff0c;简称TSDB&#xff09;是一种专门设计和优化的数据库系统&#xff0c;用于高效地存储、管理和查询带有时间戳…

【neteq】tgcall的调用

G:\CDN\P2P-DEV\Libraries\tg_owt\src\call\call.cc基本是按照原生webrtc的来的:G:\CDN\P2P-DEV\tdesktop-offical\Telegram\ThirdParty\tgcalls\tgcalls\group\GroupInstanceCustomImpl.cpptg对neteq的使用 worker 线程创建call Call的config需要neteqfactory Call::CreateAu…

C语言-链表实现贪吃蛇控制台游戏

使用C语言和链表实现贪吃蛇游戏 一、引言 贪吃蛇游戏是一个经典的游戏&#xff0c;它的玩法简单而富有挑战性。在这个博客中&#xff0c;我将分享如何使用C语言和链表数据结构来自主实现贪吃蛇游戏。我会详细介绍游戏的设计思路、编码过程、遇到的问题及解决方案&#xff0c;…

PG控制文件的管理与重建

一.控制文件位置与大小 逻辑位置&#xff1a;pgpobal 表空间中 物理位置&#xff1a;$PGDATA/global/pg_control --pg_global表空间的物理位置就在$PGDATA/global文件夹下 物理大小&#xff1a;8K 二.存放的内容 1.数据库初始化的时候生成的永久化参数&#xff0c;无法更改…

Java项目:基于SSM框架实现的在线医疗服务系统(ssm+B/S架构+源码+数据库+毕业论文+开题报告)

一、项目简介 本项目是一套基于SSM框架实现的在线医疗服务系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能…