Lifecycle 生命周期组件的那些事

news2025/8/11 14:07:06

引言

在2022的今天,AndroidX 普遍的情况下,JetPack Lifecycle 也早已经成为了开发中的基础设施,小到 View(扩展库) ,大到 Activity,都隐藏着它的身影,而了解 Lifecycle 也正是理解 JetPack 组件系列库生命感知设计的基础。

本篇定位中级,将从背景到源码实现,从而理解其背后的设计思想。

导航

看完本篇,你将会搞清以下问题:

  • Lifecycle 的前世今生;
  • Lifecycle 可以干什么;
  • Lifecycle 源码解析;

背景

在开始前,我们先聊聊关于 Lifecycle 的前世今生,从而便于我们更好的理解 Lifecycle 存在的意义。

洪荒之时

Lifecycle 之前(不排除现在😂),如果我们要在某个生命周期去执行一些操作时,经常会在Act或者Fragment写很多模版代码,如下两个示例:

  1. 比如,有一个定时器,我们需要在 Activity 关闭时将其关闭,从而避免因此导致的内存问题,所以我们自然会在 onDestory() 中去stop一下。这些事看起来似乎不麻烦,但如果是一个重复多处使用的代码,细心的开发者会将其单独抽离成为一个 case ,从而通过组合的方式降低我们主类中的逻辑,但不可避免我们依然还要存在好多模版代码,因为每次都需要 onStop() 清理或者其他操作(除非写到base,但不可接受☹️)。

📌 如果能不需要开发者自己手动,该有多好?

  1. 在老版本的友盟中,我们经常甚至需要在基类的 Activity 中复写 onResume()onPause() 等方法,这些事情说麻烦也不麻烦,但总是感觉很不舒服。不过,有经验的开发者肯定会想喷,为什么一个三方库,你就自己不会通过application.registerActivityLifecycleCallbacks 监听吗🤌🏼。

📌 注意,Application有监听全局Act生命周期的方法,Act也有这个方法。🤖

盘古开天

JetPack 之前,Android 一直秉承着传统的 MVC 架构,即 xml 作为 View, Activity 作为 ControlModel 层由数据模型或者仓库而定。虽然说官方曾经有一个MVP的示例,但真正用的人并不多。再加上官方一直也没推荐过 Android 的架构指南,这就导致传统的Android开发方式和系统的碎片化一样☹️,五花八门。随着时间的推移,眼看前端的MVVM已愈加成熟,后有Flutter,再加上开发者的需求等背景下,Google于2017年发布了新的开发架构: AAC,全名 Architecture,并且伴随着一系列相关组件,从而帮助开发者提高开发体验,降低错误概率,减少模版代码。

而本篇的主题 Lifecycle 正是其中作为基础设施的存在,在 sdk26 之后,更是被写入了基础库中。

那Lifecycle到底是干什么的呢?

Lifecycle 做的事情很简单,其就是用于检测组件(FragmentAct) 的生命周期,从而不必强依赖于 ActivityFragment ,帮助开发者降低模版代码。

常见用法

在官网中,对于 Lifecycle 的生命周期感知的整个流程如下所示:


Api介绍

相关字段

  • Event

    生命周期事件,对应具体的生命周期:

    ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_ANY

  • State

    生命周期状态节点,与 Event 紧密关联,Event 是这些结点的具体边界,换句话说,State 代表了这一时刻的具体状态,其相当于一个范围集,在这个范围里,都是这个状态。而Event代表这一状态集里面触发的具体事件是什么。

    INITIALIZED

    构造函数初始化时,且未收到 `onCreate()` 事件时的状态;
    

    CREATED

    在 `onCreate()` 调用之后,`onStop()` 调用之前的状态;
    

    STARTED

    在 `onStart()` 调用之后,`onPause()` 调用之前的状态;
    

RESUMED

    在 `onResume()` 调用时的状态;

   `DESTROYED`

   `onDestory()` 调用时的状态;

相关接口

  • LifecycleObserver

    基础 Lifecycler 实现接口,一般调用不到,可用于自定义的组件,从而避免在 Act 或者 Fragment 中的模版代码,例如ViewTreeLifecyleOwner

  • LifecycleEventObserver

    可以接受所有生命周期变化的接口;

  • FullLifecycleObserver

    用于监听生命周期变化的接口,提供了 onCreate()onStart()onPause()onStop()onDestory()onAny();

  • DefaultLifecycleObserver

    FullLifecycleObserver 的默认实现版本,相比前者,增加了默认 null 实现;


举个栗子

如下所示,通过实现 DefaultLifecycleObserver 接口,我们可以在中重写我们想要监听的生命周期方法,从而将业务代码从主类中拆离出来,且更便于复用。最后在相关类中直接使用 lifecycle.addObserver() 方法添加实例即可,这也是google推荐的用法。

上述示例中,我们使用了 viewLifecycle ,而不是 lifecycle ,这里顺便提一下。

见名之意,前者是视图(view)生命周期,后者则是非视图的生命周期,具体区别如下:

viewLifecycle 只会在 onCreateView-onDestroyView 之间有效。

lifecycle 则是代表 Fragment 的生命周期,在视图未创建时,onCreate(),onDestory() 时也会被调用到。


或者你有某个自定义View,想感知Fragment或者Act的生命周期,从而做一些事情,比如Banner组件等,与上面示例类似:

当然你也可以选择依赖:androidx.lifecycle:lifecycle-runtime 扩展库。

从而使用 view.findViewTreeLifecycleOwner() 的扩展函数获得一个 LifecycleOwner,从而在View内部自行监听生命周期,免除在Activity手动添加观察者的模版代码。

lifecycle.addObserver(view)

源码解析

Lifecycle

在官方的解释里,Lifecycle 是一个类,用于存储有关组件(Act或Fragment)声明周期状态新的类,并允许其他对象观察此类。

直接去看 Lifecycle 的源码,其实现方式如下:

总体设计如上所示,比较简单,就是观察者模式的接口模版:

使用者实现 LifecycleObserver 接口(),然后调用 addObserver() 添加到观察者列表,取消观察者时调用 rmeoveObserver() 移除掉即可。在相应的生命周期变动时,遍历观察者列表,然后通知实现了 LifecycleObserver 的实例,从而调用相应的方法。

因为其是一个抽象类,所以我们调用的一般都是它的具体实现类,也就是 LifecycleRegistry ,目前也是其的唯一实现类。


LifecycleRegistry

Lifecycle 的具体实现者,正如其名所示,主要用于管理当前订阅的 观察者对象 ,所以也承担了 Lifecycle 具体的实现逻辑。因为源码较长,所以我们做了一些删减,只需关注主流程即可,伪代码如下:

public class LifecycleRegistry extends Lifecycle {

    // 生命周期观察者map,LifecycleObserver是观察者接口,ObserverWithState具体的状态分发的包装类
    private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap;
    // 当前生命周期状态
    private State mState = INITIALIZED;
    // 持有生命周期的提供商,Activity或者Fragment的弱引用
    private final WeakReference<LifecycleOwner> mLifecycleOwner;
    // 当前正在添加的观察者数量,默认为0,超过0则认为多线程调用
    private int mAddingObserverCounter = 0;
    // 是否正在分发事件
    private boolean mHandlingEvent = false;
    // 是否有新的事件产生
    private boolean mNewEventOccurred = false;
    // 存储主类的事件state
    private ArrayList<State> mParentStates = new ArrayList<>();
​
    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        // 初始化状态,destory or init
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        // 📌 初始化实际分发状态的包装类
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        // 将观察者添加到具体的map中,如果已经存在了则返回之前的,否则创建新的添加到map中
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
        // 如果上一步添加成功了,putIfAbsent会返回null
        if (previous != null) {
            return;
        }
        // 如果act或者ff被回收了,直接return
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            return;
        }
        // 当前添加的观察者数量!=0||正在处理事件
        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        // 📌 取得观察者当前的状态
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        // 📌 如果当前观察者状态小于当前生命周期所在状态&&这个观察者已经被存到了观察者列表中
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            // 保存当前的生命周期状态
            pushParentState(statefulObserver.mState);
            // 返回当前生命周期状态对应的接下来的事件序列
            final Event event = Event.upFrom(statefulObserver.mState);
            ...
            // 分发事件
            statefulObserver.dispatchEvent(lifecycleOwner, event);
            // 移除当前的生命周期状态
            popParentState();
            // 再次获得当前的状态,以便继续执行
            targetState = calculateTargetState(observer);
        }

        // 处理一遍事件,保证事件同步
        if (!isReentrance) {
            sync();
        }
        // 回归默认值
        mAddingObserverCounter--;
    }
    ...
    static class ObserverWithState {
        State mState;
        LifecycleEventObserver mLifecycleObserver;
​
        ObserverWithState(LifecycleObserver observer, State initialState) {
            // 初始化事件观察者
            mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
            mState = initialState;
        }
​
        void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = event.getTargetState();
            mState = min(mState, newState);
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }
    }
    ...
}

我们重点关注的是 addObserver() 即订阅生命周期变更时的逻辑,具体如下:

  1. 先初始化当前观察者的状态,默认两种,即 DESTROYED(销毁) 或者 INITIALIZED(无效),分别对应 onDestory()onCreate() 之前;
  2. 初始化 状态观察者(ObserverWithState) ,内部使用 Lifecycling.lifecycleEventObserver() 将我们传递进来的 生命周期观察者(LifecycleObser) 包装为一个 生命周期[事件]观察者LifecycleEventObserver,从而在状态变更时触发事件通知;
  3. 将第二步生成的状态观察者添加到缓存map中,如果之前已经存在,则停止接下来的操作,否则继续初始化;
  4. 调用 calculateTargetState() 获得当前真正的状态。
  5. 开始事件轮训,如果 当前观察者的状态小于此时真正的状态 && 观察者已经被添加到了缓存列表 中,则获得当前观察者下一个状态,并触发相应的事件通知 dispatchEvent(),然后继续轮训。直到不满足判断条件;

需要注意的是, 关于状态的判断,这里使用了compareTo() ,从而判断当前状态枚举是否小于指定状态。


Activity中的实现

Tips:

写过权限检测库的小伙伴,应该很熟悉,为了避免在 Activity 中手动实现 onActivityRequest() ,从而实现以回调的方式获得权限结果,我们往往会使用一个透明的 Fragment ,从而将模版方法拆离到单独类中,而这种实现方式正是组合的思想。

LifecycleActivity 中的实现正是上述的方式。


如下所示,当我们在 Activity 中调用 lifecycle 对象时,内部实际上是调用了 ComponentActivity.mLifecycleRegistry,具体逻辑如下:

不难发现,在我们的 Activity 初始化时,相应的 LifecycleRegistry 已经被初始化。

在上面我们说过,为了避免对基类的入侵,我们一般会用组合的方式,所以这里的 ReportFragment 正是 LifecycleActivity 中具体的逻辑承载方,具体逻辑如下:

ReportFragment.injectIfNeededIn

内部会对sdk进行判断,对应着两套流程,对于 sdk>=29 的,通过注册 Activity.registerActivityLifecycleCallbacks() 事件实现监听,对于 sdk<29 的,重写 Fragment 相应的生命周期方法完成。

ReportFragment 具体逻辑如下:


Fragment中的实现

直接去 Fragment.lifecycle 中看一下即可,伪代码如下:

总结如下:lifecycle 实例会在 Fragment 构造函数 中进行初始化,而 mViewLifecycleOwner 会在 performCreateView() 执行时初始化,然后在相应的 performXxx 生命周期执行时,调用相应的 lifecycle.handleLifecycleEvent() 从而完成事件的通知。

总结

Lifecycle 作为 JetPack 的基石,而理解其是我们贯穿相应生命周期的关键所在。

关于生命周期的通知,Lifecycle 并没有采用直接通知的方式,而是采用了 Event(事件) + State(状态) 的设计方式。

  • 对于外部生命周期订阅者而言,只需要关注事件 Event 的调用;
  • 而对于Lifecycle而言,其内部只关注 State ,并将生命周期划分为了多个阶段,每个状态又代表了一个事件集,从而对于外部调用者而言,无需拘泥于当前具体的状态是什么。

在具体的实现底层上面:

  • Activity 中,采用组合的方式而非继承,在 Activity.onCreate() 触发时,初始化了一个透明Fragment,从而将逻辑存于其中。对于sdk>=29的版本,因为 Activity 本身有监听生命周期的方法,从而直接注册监听器,并在相应的会调用触发生命周期事件更新;对于sdk<29的版本,因为需要兼容旧版本,所以重写了 Fragment 相应的生命周期方法,从而实现手动触发更新我们的生命周期观察者。
  • Fragment 中,会在 Fragment 构造函数中初始化相应的 Lifecycle ,并重写相应的生命周期方法,从而触发事件通知,实现生命周期观察者的更新。

每当我们调用 addObserver() 添加新的观察者时:

内部都会对我们的观察者进行包装,并将其包装为一个具体的事件观察者 LifecycleEventObserver,以及生成当前的观察者对应的状态实例(内部持有LifecycleEventObserver),并将其保存到 缓存map 中。接着会去对比当前的 观察者的状态lifecycle此时实际状态 ,如果 当前观察者状态<lifecycle对应的状态 ,则触发相应 Event 的通知,并 更新此观察者对应的状态 ,不断轮训,直到当前观察者状态 >= lifecycle 对应状态。

更多Android 知识点可参考

Android 性能调优系列https://0a.fit/dNHYY

Android 车载学习指南https://0a.fit/jdVoy

Android Framework核心知识点笔记https://0a.fit/acnLL

Android 八大知识体系https://0a.fit/mieWJ

Android 中高级面试题锦https://0a.fit/YXwVq

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

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

相关文章

D-013 蜂鸣器硬件电路设计

蜂鸣器硬件电路设计1 简介2 蜂鸣器电路设计1 简介 蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中做发声器件。 蜂鸣器的分类&#xff1a;压电…

【ASM】字节码操作 工具类与常用类 AnalyzerAdapter初步介绍

文章目录 1.概述1.1 第一句话1.2 第二句话2. AnalyzerAdapter2.1 class info2.2 fields2.3 constructors2.4 methods2.4.1 execute方法2.4.2 return和throw2.4.3 jump2.4.4 visitFrame方法2.4.5 new和invokespecial1.概述 在上一篇文章:

创建KVM虚拟机公共镜像

手动配置kvm教程&#xff1a; https://blog.csdn.net/nvd11/article/details/127435990 但是毕竟每次创建新的虚拟机时&#xff0c;还要装一次系统比较麻烦。 打算做个公共的ubuntu镜像出来。 目标&#xff1a; NoDESCcpu数内存第一步创建虚拟机vm128g第二步创建虚拟机镜像文…

第五章《类的继承》第1节:继承的概念与实现方式

继承是面向对象最显著的一个特性,它是以已存在的类为基础定义新类的技术,新的类可以继承已有类的属性和方法,也可以增加新的类成员。 5.1.1什么是继承 在现实生活中,一类事物往往都可以划分成更小的类别。例如:“人”就是一类事物,按照人的身份和职业,可以把人划分成学…

SSTV慢速扫描的几种模式优劣对比

下面使用的图片是我自己拍摄的照片,无版权问题 原图: 扫描环境: 家中客厅,窗户打开,窗外有鸟叫和人声.模拟了介于野外和户外之间的环境 ,两部设备相隔1厘米,音量35%,所有测试下来设备未移动. Martin1 模式 : 扫描时长: 1分55秒 扫描后图片 Martin2 模式 : 扫描时长: 58秒…

Pywinauto的安装及使用

目录 1.pywinauto简单介绍 2.pywinauto的安装 3. 通过启动windows自带记事本&#xff0c;检查是否可正常使用 1.pywinauto简单介绍 pywinauto是一组用于自动化测试脚本的python模块&#xff0c;用于自动化Microsoft Windows GUI。它允许将鼠标和键盘操作发送到windows对话框…

基于JSP的企业固定资产管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86466989 主要使用技术 SpringStruts2HibernateJSPCSSJSMysql 功能介绍 固定资产管理系统是对高校固定资产的一个信息化管理系统&#xff0c;基本功能包括&#xff1a; 对固定资产的购进、…

基于遥感和GIS技术的生态承载力评价的解决方案

生态承载力的概念最早来自于生态学。1921年&#xff0c;Park和Burgess在生态学领域中首次应用了生态承载力的概念&#xff0c;即在某一特定环境条件下&#xff08;主要指生存空间、营养物质、阳光等生态因子的组合&#xff09;&#xff0c;某种个体存在数量的最高极限。生态承载…

2023-2028年中国化肥行业市场需求预测与投资趋势分析报告

本报告由锐观咨询重磅推出&#xff0c;对中国化肥行业的发展现状、竞争格局及市场供需形势进行了具体分析&#xff0c;并从行业的政策环境、经济环境、社会环境及技术环境等方面分析行业面临的机遇及挑战。还重点分析了重点企业的经营现状及发展格局&#xff0c;并对未来几年行…

m分集2跳OFDM系统中基于功率分配和子载波配对算法的信道容量matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 随着当代无线通信事业的迅猛发展,无线频谱资源已显得越来越匮乏,传统固定静态的无线频谱分配模式和策略,很难为未来的无线通信事业的进一步发展提供更多可利用的无线频谱带宽和更高的频谱…

源码分析 Laravel 重复执行同一个队列任务的原因

发现问题 在 Laravel 中使用 Redis 处理队列任务&#xff0c;框架提供的功能非常强大&#xff0c;但是最近遇到一个问题&#xff0c;就是发现一个任务被多次执行&#xff0c;这是为什么呢&#xff1f; 先说原因&#xff1a; 因为在 Laravel 中如果一个队列&#xff08;任务&a…

Java项目:JSP民宿预订网站信息管理平台

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为前后台&#xff0c;包括管理员与普通用户两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登录,用户管理,会员管理,…

高斯消元总结

A-Matrix Equation_第 45 届国际大学生程序设计竞赛&#xff08;ICPC&#xff09;亚洲区域赛&#xff08;济南&#xff09;&#xff08;重现赛&#xff09; 自己写一个2维矩阵或者3维矩阵就可以发现对于每一列来说都是独立的&#xff0c;每一列的n个Cij都是都关系的&#xff0c…

Sentinel熔断降级

Sentinel熔断降级 1、介绍 官网地址&#xff1a;https://github.com/alibaba/Sentinel/wiki/熔断降级 Sentinel 提供以下几种熔断策略&#xff1a; 慢调用比例 (SLOW_REQUEST_RATIO)&#xff1a;选择以慢调用比例作为阈值&#xff0c;需要设置允许的慢调用 RT&#xff08;即最…

cesium for ue5中actor的cesium georeference组件的改变

今天&#xff0c;重新回顾下cesium for ue的教程&#xff0c;actor添加cesium georeference组件 发现在ue5中&#xff0c;没有这个组件了&#xff0c; 忍住卸载ue5&#xff0c;重新安装ue4.27的冲动&#xff0c;cesium的组件数量增多了&#xff0c;反而功能减少了&#xff…

hive的安装配置及使用

hive需要MySQL和hadoop的支持 文章目录hive需要MySQL和hadoop的支持安装完毕MySQL之后下载并解压hive添加系统环境变量配置hive修改 vim hive-env.sh修改hive-site.xml(1、简化配置&#xff08;推荐&#xff09;)修改hive-site.xml(2、在简化的基础上添加了部分配置)下载一个my…

Java项目:JSP药店药品商城管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;分为管理员与普通用户两种角色&#xff0c;管理员登录后台&#xff0c;普通用户登录前台&#xff1b; 管理员角色…

VMware使用教程

文章目录下载软件安装软件新建虚拟机自定义安装下载软件 下载VMware软件&#xff08;应该是需要许可证/密钥的&#xff0c;时间长有点忘了&#xff0c;当然建议买&#xff0c;拒绝白嫖&#xff0c;保护产权&#xff08;可以直接百度搜doge&#xff09;&#xff09;。 安装软件…

Sentinel流控规则

Sentinel流控规则 1、基本介绍 资源名&#xff1a;唯一名称&#xff0c;默认请求路径(如&#xff1a;http://localhost:8089/testA) 针对来源&#xff1a;Sentinel可以针对调用者进行限流&#xff0c;填写微服务名&#xff0c;指定对哪个微服务进行限流 &#xff0c;默认defa…

Bioinformatics2021 | FraGAT+:基于分子原始片段的多尺度图注意力机制的分子性质预测模型

原文标题&#xff1a;FraGAT: a fragment-oriented multi-scale graph attention model for molecular property prediction 论文地址&#xff1a;FraGAT: a fragment-oriented multi-scale graph attention model for molecular property prediction | Bioinformatics | Oxfo…