当前位置: 首页 > news >正文

【Android Framework系列】第14章 Fragment核心原理(AndroidX版本)

1 简介

Fragment是一个历史悠久的组件,从API 11引入至今,已经成为Android开发中最常用的组件之一。
Fragment表示应用界面中可重复使用的一部分。Fragment定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。Fragment不能独立存在,而是必须由Activity或另一个Fragment托管。Fragment的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。
本章节主要探索Fragment生命周期状态事务管理

2 Fragment生命周期

2.1 Fragment完整生命周期

onAttach -> onCreate -> onCreatedView -> onActivityCreated -> onStart -> onResume -> onPause -> onStop -> onDestroyView -> onDestroy -> onDetach
如下图所示:
在这里插入图片描述
Fragment与Activity生命周期各状态的对比:
在这里插入图片描述

2.2 Fragment生命周期状态

Fragment的生命周期状态只有5个,通过降序以及升序来进行判断。如果是升序,走显示的生命周期,降序为销毁的生命周期。由于Fragment的版本代码不断在更新,状态机也不断在变化,因此我们主要分析重点的状态机思路

INITIALIZED:Fragment 的一个新实例已实例化。
CREATED:系统已调用第一批 Fragment 生命周期方法。在 Fragment 处于此状态期间,系统也会创建与其关联的视图。
STARTED:Fragment 在屏幕上可见,但没有焦点,这意味着其无法响应用户输入。
RESUMED:Fragment 可见并已获得焦点。
DESTROYED:Fragment 对象已解除实例化。

FragmentManagerImpl利用mCurState成员变量来标记当前状态,Fragment利用mState成员变量来标记当前状态。
更新FragmentManagerImpl的生命周期状态,这里以FragmentActivity的生命周期回调开始,先看派发给FragmentManagerImpl各个状态的时机。
FragmentManagerImpl继承自FragmentManagerFragmentActivity通过生命周期调用Fragment的生命周期,其实就是调用了FragmentManagerImpl这个类来进行分发,最终都是走的dispatchStateChange()方法进行状态机的更新。

FragmentActivity.java

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);mFragments.dispatchCreate();}

我们以FragmentActivity#onCreate()方法为例,可以看到调用了mFragmentsdispatchCreate()方法。然后调用FragmentManager的dispatchCreate()方法。

FragmentManager.java

//(源码方法跳转太多,我直接帮你梳理出核心流程,跟你直接看源码会不同,但逻辑是相同的)
public void dispatchCreate() {mStateSaved = false;mStopped = false;moveToState(Fragment.CREATED, false);// 4、处理未执行的事务execPendingActions();
}void moveToState(int newState, boolean always) {// 1、状态判断if (nextState == mCurState) {return;}mCurState = nextState;// 2、执行添加的 Fragment// Must add them in the proper order. mActive fragments may be out of orderfor (int i = 0; i < mAdded.size(); i++) {Fragment f = mAdded.get(i);// 更新 Fragment 到当前状态moveFragmentToExpectedState(f);}// 3、执行未添加,但是准备移除的 Fragment// Now iterate through all active fragments. These will include those that are removed and detached.for (int i = 0; i < mActive.size(); i++) {Fragment f = mActive.valueAt(i);if (f != null && (f.mRemoving || f.mDetached) && !f.mIsNewlyAdded) {// 更新 Fragment 到当前状态moveFragmentToExpectedState(f);}}
}

其中,moveFragmentToExpectedState() 最终调用到moveToState(Fragment, int)

// moveFragmentToExpectedState 最终调用到 
// 更新 Fragment 到当前状态
void moveToState(Fragment f, int newState) {// 1、准备 Detatch Fragment 的情况,不再与宿主同步,进入 CREATED 状态if ((!f.mAdded || f.mDetached) && newState > Fragment.CREATED) {newState = Fragment.CREATED;}// 2、移除 Fragment 的情况,Fragment 不再与宿主同步if (f.mRemoving && newState > f.mState) {if (f.isInBackStack()) {// 2.1 移除动作添加到返回栈,则进入 CREATED 状态newState = Math.min(nextState, Fragment.CREATED);} else {// 2.1 移除动作添加到返回栈,则进入 DESTROY 状态newState = Math.min(nextState, Fragment.INITIALIZING);}}// 3、真正执行状态转移if (f.mState <= newState ) {switch (f.mState) {case Fragment.INITIALIZING:if (nextState> Fragment.INITIALIZING) {...}// fall throughcase Fragment.CREATED:...// fall throughcase Fragment.ACTIVITY_CREATED:...// fall throughcase Fragment.STARTED:...}} else {switch (f.mState) {case Fragment.RESUMED:if (newState < Fragment.RESUMED) {...}// fall throughcase Fragment.STARTED:...// fall throughcase Fragment.ACTIVITY_CREATED:...// fall throughcase Fragment.CREATED:...}}...
}
  1. 小伙伴们有没发现上面代码的特别之处?case里面没有break
    这样的好处,是为了让Fragment走完整的生命周期
  2. 触发状态转移时,首先会判断Fragment,如果已经处于目标状态newState,则会跳过状态转移。然而,并不是FragmentManager里所有的Fragment都会执行状态转移,只有「mAdded为真&&mDetached为假」Fragment才会更新到目标状态,其他Fragment会脱离宿主状态。最后,状态转移完成后会处理未执行的事务execPendingActions();,可见每次dispatchXXX()都会提供一次事务执行的窗口。
    不同Fragment标志位(Detach/Remove/返回栈)与最终状态的关系总结如下表:
    在这里插入图片描述
    提示: 这些标志位可以通过事务进行干涉。

2.3 Fragment的生命周期对应状态

升序:
onCreate():Fragment已实例化并处于CREATED状态。不过,其对应的视图尚未创建。
onCreateView():此方法可用于创建布局。Fragment已进入CREATED状态
onViewCreated():此方法在创建视图后调用。在此方法中,您通常会通过调用findViewById()将特定视图绑定到属性。
onStart():Fragment已进入STARTED状态
onResume():Fragment已进入RESUMED状态,现已具有焦点(可响应用户输入)。

降序:
onPause():Fragment已重新进入STARTED状态。相应界面对用户可见。
onStop():Fragment已重新进入CREATED状态。该对象已实例化,但它在屏幕上不再显示。
onDestroyView():该方法在Fragment进入DESTROYED状态之前调用。视图已从内存中移除,但Fragment对象仍然存在。
onDestroy():Fragment进入DESTROYED状态

下图总结了Fragment生命周期以及状态之间的转换:
在这里插入图片描述

3 Fragment 事务管理

下面我们来了解下影响 Fragment 状态转移的第二个因素:事务

3.1 事务概述

  1. 事务的特性是什么?
    事务是恢复和并发的基本单位,具备4个基本特性:
    原子性:事务不可分割,要么全部完成,要么全部失败回滚;
    一致性:事务执行前后数据都具有一致性;
    隔离性:事务执行过程中,不受其他事务干扰;
    持久性:事务一旦完成,对数据的改变就是永久的。在Android中体现为Fragment状态保存后,commit()提交事务会抛异常,因为这部分新提交的事务影响的状态无法保存。

  2. 事务的作用是什么?
    使用事务FragmentTransaction可以动态改变Fragment状态,使得Fragment在一定程度脱离宿主的状态。不过,事务依然受到宿主状态约束,例如:当前Activity处于STARTED状态,那么addFragment不会使得Fragment进入RESUME状态。只有将来Activity进入RESUME状态时,才会同步Fragment到最新状态。

3.2 不同事务操作的区别

  1. add&remove:Fragment状态在INITIALIZINGRESUMED之间转移;
  2. detach&attach:Fragment状态在CREATERESUMED之间转移;
  3. replace:先移除所有containerId中的实例,再add一个Fragment;
  4. show&hide:只控制Fragment隐藏显示,不会触发状态转移,也不会销毁Fragment视图或实例;
  5. hide&detach&removehide不会销毁视图和实例、detach只销毁视图不销毁实例、remove会销毁实例(自然也销毁视图)。不过,如果remove的时候将事务添加到回退栈,那么Fragment实例就不会被销毁,只会销毁视图。
    在这里插入图片描述
    需要注意detach Fragment 并不会回调 onDetach(),而是转移到CREATE 状态,回调 onDetach() 需要转移到 INITIALIZING(是不是很奇葩的起名!)

3.3 不同事务提交方式

FragmentTransaction 定义了 5 种提交方式:
在这里插入图片描述
需要注意的地方:

  1. onSaveInstanceState()保存状态后,事务形成的新状态是不会被保存的。在状态保存之后调用 commit()commitNow()会抛异常,我们需要使用commitAllowingStateLoss()commitNowAllowingStateLoss()进行提交,我们可以看下抛异常的具体代码:

FragmentManagerImpl.java

private void checkStateLoss() {if (mStateSaved || mStopped) {throw new IllegalStateException("Can not perform this action after onSaveInstanceState");}
}
  1. 使用commitNow()commitNowAllowingStateLoss()提交的事务不允许加入回退栈

为什么有这个设计呢?可能是 Google 考虑到同时存在同步提交和异步提交的事务,并且两个事务都要加入回退栈时,无法确定哪个在上哪个在下是符合预期的,所以干脆禁止 commitNow() 加入回退栈(这里记住带Now的提交为同步提交,不加入回退栈中)。如果确实有需要同步执行+回退栈的应用场景,可以采用commit() + executePendingTransactions()的取巧方法。相关源码体现如下:

BackStackRecord.java

@Override
public void commitNow() {disallowAddToBackStack();mManager.execSingleAction(this, false);
}@Override
public void commitNowAllowingStateLoss() {disallowAddToBackStack();mManager.execSingleAction(this, true);
}
@NonNull
public FragmentTransaction disallowAddToBackStack() {if (mAddToBackStack) {throw new IllegalStateException("This transaction is already being added to the back stack");}mAllowAddToBackStack = false;return this;
}  
  1. commitNow()executePendingTransactions() 都是同步执行,有区别吗?
    commitNow()是同步执行当前事务,而executePendingTransactions()是同步执行事务队列中的全部事务。

4 Fragment逻辑流程

Fragment的逻辑流程本章节不做详细分析,贴上调用关系图供小伙伴们参考学习。

FragmentActivity到FragmentManager的相关调用关系图:
在这里插入图片描述
FragmentManager和BackStackRecord回退栈项的调用关系图:
在这里插入图片描述

5 总结

本章节我们主要了解Fragment的两大模块:

  1. 生命周期及状态 :生命周期状态密切相关,状态机升序降序来实现生命周期的对应。
  2. 事务管理:commit()commitNow()commitAllowingStateLoss()commitNowAllowingStateLoss()executePendingTransactions()的区别分析。

相关文章:

【Android Framework系列】第14章 Fragment核心原理(AndroidX版本)

1 简介 Fragment是一个历史悠久的组件&#xff0c;从API 11引入至今&#xff0c;已经成为Android开发中最常用的组件之一。 Fragment表示应用界面中可重复使用的一部分。Fragment定义和管理自己的布局&#xff0c;具有自己的生命周期&#xff0c;并且可以处理自己的输入事件。…...

Python 网页爬虫原理及代理 IP 使用

目录 前言 一、Python 网页爬虫原理 二、Python 网页爬虫案例 步骤1&#xff1a;分析网页 步骤2&#xff1a;提取数据 步骤3&#xff1a;存储数据 三、使用代理 IP 四、总结 前言 随着互联网的发展&#xff0c;网络上的信息量变得越来越庞大。对于数据分析人员和研究人…...

失效的访问控制及漏洞复现

失效的访问控制(越权) 1. 失效的访问控制(越权) 1.1 OWASP TOP10 1.1.1 A5:2017-Broken Access Control 未对通过身份验证的用户实施恰当的访问控制。攻击者可以利用这些缺陷访问未经授权的功能或数据&#xff0c;例如&#xff1a;访问其他用户的帐户、查看敏感文件、修改其…...

MLOps:掌握机器学习部署:Docker、Kubernetes、Helm 现代 Web 框架

介绍&#xff1a; 在机器学习的动态世界中&#xff0c;从开发模型到将其投入生产的过程通常被认为是复杂且多方面的。 然而&#xff0c;随着 Docker、Kubernetes 等工具以及 FastAPI、Streamlit 和 Gradio 等用户友好的 Web 框架的出现&#xff0c;这一过程变得比以往更加简化…...

Python标识符命名规范

简单地理解&#xff0c;标识符就是一个名字&#xff0c;就好像我们每个人都有属于自己的名字&#xff0c;它的主要作用就是作为变量、函数、类、模块以及其他对象的名称。 Python 中标识符的命名不是随意的&#xff0c;而是要遵守一定的命令规则&#xff0c;比如说&#xff1a;…...

对 fastq 和 bam 进行 downsample

对 fastq 和 bam 进行 downsample 一、Fastq1、seqtk二、Bam1、samtools2、Picard DownsampleSam3、比较 并行采样模板 一、Fastq 1、seqtk Seqtk 是一种快速轻量级的工具&#xff0c;用于处理 FASTA 或 FASTQ 格式的序列。 它可以无缝解析 FASTA 和 FASTQ 文件&#xff0c;这…...

网络爬虫:如何有效的检测分布式爬虫

分布式爬虫是一种高效的爬虫方式&#xff0c;它可以将爬虫任务分配给多个节点同时执行&#xff0c;从而加快爬虫的速度。然而&#xff0c;分布式爬虫也容易被目标网站识别为恶意行为&#xff0c;从而导致IP被封禁。那么&#xff0c;如何有效地检测分布式爬虫呢&#xff1f;本文…...

elementUI可拖拉宽度抽屉

1&#xff0c;需求&#xff1a; 在elementUI的抽屉基础上&#xff0c;添加可拖动侧边栏宽度的功能&#xff0c;实现效果如下&#xff1a; 2&#xff0c;在原组件上添加自定义命令 <el-drawer v-drawerDrag"left" :visible.sync"drawerVisible" direc…...

OpenPCDet系列 | 8.4 nuScenes数据集数据调用和数据分析

文章目录 1. 对数据集遍历1.1 统计mini版本的nuScenes各模态数据和关键帧的数量1.2 单独遍历lidar模态数据1.3 遍历scene统计数据1.4 遍历sample统计数据1.5 遍历sample_data统计数据1.6 数据集的底层结构2. 对数据集可视化2.1 render_sample和render_sample_data2.2 nusc.rend…...

WeiTitlePopupWindow

目录 1 WeiTitlePopupWindow 1.1 // 设置可点击 1.2 // 设置弹窗外可点击 1.3 // 设置弹窗宽度和高度 1.4 // 设置弹窗布局界面 WeiTitlePopupWindow // 设置可点击setTouchable(true);...

qemu/kvm学习笔记

qemu/kvm架构 cpu虚拟化的示例 Reference: kvmtest.c [LWN.net] 主要步骤&#xff1a; QEMU通过/dev/kvm设备文件发起KVM_CREATE_VM ioctl&#xff0c;请求KVM创建一个虚拟机。KVM创建虚拟机相应的结构体&#xff0c;并为QEMU返回一个虚拟机文件描述符QEMU通过虚拟机文件描述…...

android 车载widget小部件部分详细源码实战开发-千里马车载车机framework开发实战课程

官网参考链接&#xff1a;https://developer.android.google.cn/develop/ui/views/appwidgets/overview 1、什么是小部件 App widgets are miniature application views that can be embedded in other applications (such as the home screen) and receive periodic updates…...

如何使用CSS画一个三角形

原理&#xff1a;其实就是规定元素的四个边框颜色及边框宽度&#xff0c;将元素宽高设置为0。如果要哪个方向的三角形&#xff0c;将对应其他三个方向的边框宽和颜色设置为0和透明transparent即可 1.元素设置边框&#xff0c;宽高&#xff0c;背景色 <style>.border {w…...

第15章_锁: (表级锁、页级锁、行锁、悲观锁、乐观锁、全局锁、死锁)

3.2 从数据操作的粒度划分&#xff1a;表级锁、页级锁、行锁 为了提高数据库并发度&#xff0c;每次锁定的数据范围越小越好&#xff0c;理论上每次只锁定当前操作的数据的方案会得到最大的并发度&#xff0c;但管理锁是很耗资源&#xff08;涉及获取、检查、释放锁等动作)。因…...

python音频转文字调用baidu

python音频转文字调用的是百度智能云的接口&#xff0c;因业务需求会涉及比较多数字&#xff0c;所以这里做了数字的处理&#xff0c;可根据自己的需求修改。 from flask import Flask, request, jsonify import requestsfrom flask_limiter import Limiterapp Flask(__name_…...

靶场溯源第二题

关卡描述&#xff1a;1. 网站后台登陆地址是多少&#xff1f;&#xff08;相对路径&#xff09; 首先这种确定的网站访问的都是http或者https协议&#xff0c;搜索http看看。关于http的就这两个信息&#xff0c;然后172.16.60.199出现最多&#xff0c;先过滤这个ip看看 这个很…...

mysql 的增删改查以及模糊查询、字符集语句的使用

一、mysql启动与登陆(windows下的mysql操作) 1.启动mysql服务 net start mysql81 2.登陆mysql mysql -uroot -p 3.查看所有数据库 show databases; 二、模糊查询&#xff08;like&#xff09; 1. _代表查询单个 2.%代表查询多个 3.查找所有含有schema的数据库&#xff1b;…...

Python Django框架中文教程:学习简单、灵活、高效的Web应用程序框架

概述: Python Django是一种流行的Web应用程序框架&#xff0c;被广泛应用于开发高效、可扩展的网站和Web应用程序。Django以其简单、灵活和高效而受到开发者们的青睐。它提供了强大的工具和功能&#xff0c;使开发过程更加容易和高效。 Django的主要目标是帮助开发者快速构建…...

Docker认识即安装

Docker及相关概念 Docker和虚拟机方式的区别&#xff1a;虚拟机技术是虚拟出一套硬件后&#xff0c;在其上运行一个完整的操作系统&#xff0c;在该系统上在运行所需应用进程&#xff1b;而容器内的应用进程是直接运行于宿主的内核&#xff0c;容器内没有自己的内核&#xff0…...

chrome 谷歌浏览器 导出插件拓展和导入插件拓展

给同事部署 微软 RPA时&#xff0c;需要用到对应的chrome浏览器插件&#xff1b;谷歌浏览器没有外网是不能直接下载拓展弄了半小时后才弄好&#xff0c;竟发现没有现成的教程&#xff0c;遂补充&#xff1b; 如何打包导出 谷歌浏览器 地址栏敲 chrome://extensions/在对应的地…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...