【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()的区别分析。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录笔记--回溯算法篇

1--回溯算法理论基础 回溯算法本质上是一个暴力搜索的过程&#xff0c;其常用于解决组合、切割、子集、排列等问题&#xff0c;其一般模板如下&#xff1a; void backTracking(参数){if(终止条件){// 1. 收获结果;// 2. return;}for(..遍历){// 1. 处理节点// 2. 递归搜索// 3.…

K8S 基础概念学习

1.K8S 通过Deployment 实现滚动发布&#xff0c;比如左边的ReplicatSet 的 pod 中 是V1版本的镜像&#xff0c;Deployment通过 再启动一个 ReplicatSet 中启动 pod中 镜像就是V2 2.每个pod 中都有一个pause 容器&#xff0c;他会连接本pod中的其他容器&#xff0c;实现互通。p…

【Java】基于SSM的单位人事管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

[Linux]动静态库

[Linux]动静态库 文章目录 [Linux]动静态库见一见库存在库的原因编写库模拟编写静态库模拟使用静态库模拟编写动态库模拟使用静态库 库的加载原理静态库的加载原理动态库的加载原理 库在可执行程序中的编址策略静态库在可执行程序中的编址策略动态库在可执行程序中的编址策略 见…

储能直流侧计量表DJSF1352

安科瑞 华楠 具有CE/UL/CPA/TUV认证 DJSF1352-RN导轨式直流电能表带有双路直流输入&#xff0c;主要针对电信基站、直流充电桩、太阳能光伏等应用场合而设计&#xff0c;该系列仪表可测量直流系统中的电压、电流、功率以及正反向电能等。在实际使用现场&#xff0c;即可计量总…

LT8711HE 是一款高性能的Type-C/DP1.2到HDMI2.0转换器

概述&#xff1a; LT8711HE是一种高性能的Type-C/DP1.2到HDMI2.0转换器&#xff0c;设计用于连接USB Type-C源或DP1.2源到HDMI2.0接收器。LT8711HE集成了一个DP1.2兼容的接收器&#xff0c;和一个HDMI2.0兼容的发射机。此外&#xff0c;还包括两个CC控制器&#xff0c;用于CC通…

自然语言处理——数据清洗

一、什么是数据清洗 数据清洗是指发现并纠正数据文件中可识别的错误的最后一道程序&#xff0c;包括检查数据一致性&#xff0c;处理无效值和缺失值等。与问卷审核不同&#xff0c;录入后的数据清理一般是由计算机而不是人工完成。 ——百度百科 二、为什么要数据清洗 现实生…

bboss 流批一体化框架 与 数据采集 ETL

数据采集 ETL 与 流批一体化框架 特性&#xff1a; 高效、稳定、快速、安全 bboss 是一个基于开源协议 Apache License 发布的开源项目&#xff0c;主要由以下三部分构成&#xff1a; Elasticsearch Highlevel Java Restclient &#xff0c; 一个高性能高兼容性的Elasticsea…

java开发之个人微信的二次开发

简要描述&#xff1a; 修改我在某群的昵称 请求URL&#xff1a; http://域名/updateIInChatRoomNickName 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参…

Python标准数据类型-List(列表)

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;零基础入门篇 &#x1f4ac;个人格言&#xff1a;不断的翻越一座…

备份StarRocks数据到对象存储minio中/外表查minio中的数据

1.部署minio环境 docker pull minio/minio宿主机与容器挂在映射 宿主机位置容器位置/data/minio/config/data/data/minio/data/root/.minio 拉起环境&#xff1a; docker run -p 9000:9000 -p 9090:9090 --name minio \ -d --restartalways \ -e "MINIO_ACCESS_KEYadm…

基于Dubbo实现服务的远程调用

目录 前言 RPC思想 为什么使用Dubbo Dubbo技术框架 ​编辑 调用关系流程 基础实现 A.提供统一业务Api B.编辑服务提供者Product B.a 添加依赖 B.b 添加Dubbo 配置(基于yaml配置文件) B.c 编写并暴露服务 C.编辑服务消费者 C.a 添加依赖 C.b 添加Dubbo配置 C.c 引用…

使用正则表达式总结

多行匹配 使用Pattern.DOTALL | Pattern.MULTILINE参数 Pattern.CASE_INSENSITIVE&#xff1a;不区分大小写 public static void main(String[] args) {String teststr "AA aa AASSF \n\r */ DDET AA";String regStr "(?AA)\\w\\b";extracted(testst…

网络威胁防御+资产测绘系统-Golang开发

NIPS-Plus 网络威胁防御资产测绘系统-Golang开发 项目地址&#xff1a;https://github.com/jumppppp/NIPS-Plus NIPS-Plus 是一款使用golang语言开发的网络威胁防御系统&#xff08;内置资产测绘系统&#xff09; 网络威胁流量视图网络威胁详细信息浏览列表网络威胁反制探测攻…

C++ std::pair and std::list \ std::array

std::pair<第一个数据类型, 第二个数据类型> 变量名 例如&#xff1a; std::pair<int, string> myPair; myPair.first;拿到第一个int变量 myPair.second拿到第二个string变量 std::pair需要引入库#include "utility" std::make_pair() 功能制作一个…

数控程序传输DNC服务、数控刀补服务(发那科fanuc、西门子、三菱、广数、新代、华中、宝元、马扎克、大畏Okuma)等数据采集服务

行业现状&#xff1a; 最近听到很多做MES、ERP这一行的叫苦&#xff0c; 客户对项目的要求越来越严格&#xff0c;做到数字化工厂都伴随着ERP、MES的项目要求必须一起做下去 然而很对MES、ERP对设备协议不懂&#xff0c;买了协议自己还要开发&#xff0c;考虑线程的问题、断…

syn洪流原理

TCP三次握手 建立连接发送或回应第一次握手客户端发送报文&#xff0c;标志位为SYN&#xff08;seqa&#xff09;第二次握手服务器发送报文&#xff0c;标志位为SYN&#xff0c;ACK&#xff08;seqb,acka1&#xff09;第三次握手客户端回应服务器报文&#xff0c;标志位为ACK&…

Unity 之 Invoke 与InvokeRepeting 函数控制定时调用

文章目录 InvokeInvokeRepeating Invoke 在Unity游戏开发中&#xff0c;Invoke是一种用于延迟调用方法的方法。它允许你在一定的时间之后执行特定的函数或方法&#xff0c;通常用于执行定时任务&#xff0c;例如在一段时间后触发一个事件或在一定间隔内重复执行某个方法。Invo…

图的应用(最小生成树,最短路径,有向无环图)

目录 一.最小生成树 1.生成树 2.无向图的生成树 3.最小生成树算法 二.最短路径 1.单源最短路径---Dijkstra&#xff08;迪杰斯特拉&#xff09;算法 2.所有顶点间的最短路径---Floyd&#xff08;弗洛伊德&#xff09;算法 三.有向无环图的应用 1.AOV网&#xff08;拓扑…

Opencv手工选择图片区域去水印

QT 插件化图像算法研究平台的功能在持续完善&#xff0c;补充了一个人工选择图片区域的功能。 其中&#xff0c;图片选择功能主要代码如下&#xff1a; QRect GLImageWidget::getSeleted() {QRect ajust(0,0,0,0);if(image.isNull() || !hasSelection)return ajust;double w1…