降Compose十八掌之『羝羊触蕃』| Handle Platform Lifecycles

news2024/9/26 3:25:55

公众号「稀有猿诉」        原文链接 降Compose十八掌之『羝羊触蕃』| Handle Platform Lifecycles

Jetpack Compose是一个独立的声明式UI框架,它并不受限于任何操作系统平台,从框架定位的角度来讲,它是跨平台的,也应该要跨平台。但是我们的应用程序必然是为某些操作系统平台(后面简称平台Platform)构建的,也就是说要运行在某些平台上面。这就免不了要与平台进行打交道。这篇文章将以Android平台为例,学习在Compose中如何处理平台的生命周期事件。

banner

感知平台生命周期事件

对于移动应用程序来说,感知平台的生命周期是非常重要的,比如最为典型的场景,对于一个新闻消息类的应用来说,当首次进入页面的时候肯定 要刷新拉取最新的消息,当用户切换到另外一个应用时,比如接了个电话,或者分享,之后再回到你的应用页面,这时也应该主动刷新消息,而不是要等着用户手动的去点击刷新按扭;再比如说当使用了硬件资源(位置,Camera或者Sensors等)时,更是要当离开应用页面的时候就应该立即释放硬件,以停止对硬件资源的占用。

从前面的文章降Compose十八掌之『损则有孚』| Lifecycle中我们了解到Composable本身的生命周期与平台是无关的且非常不一致,光靠Compose自己的节奏是无法感知到在平台生命周期事件的。这就需要我们使用一些桥接工具来感知平台生命周期事件,以能让我们针对感兴趣的事件执行一些操作。

生命周期事件副作用函数(LifecycleEffects)

幸运的是Jetpack组件中的lifecycle已经添加了对Compose的支持,定义了一些生命周期副作用函数,在这些副作用函数中可以针对 不同的事件设置代码块,当相应的生命周期发生时就会执行这些代码块:

LifecycleEventEffect(Lifecycle.Event.ON_START) {
  // onStar时执行一些操作
}

上面的代码就是指定要在onStart时做一些事情。需要注意的是,无法监听onDestroy(即事件Lifecycle.Event.ON_DESTROY),因为Compose的组合会在onDestroy之前就结束了。

除了上面的用法之外,还有更为为方便的LifecycleStartEffect和LifecycleResumeEffect可以直接使用,它们是针对onStart/onStop和onResume/onPause两对事件的,因为生命周期中最为常用的就是这四个事件了:

LifecycleStartEffect {
  // onStart中需要做的事情

  onStopOrDispose {
    // onStop需要做的事情
  }
}

LifecycleResumeEffect {
  // onResume需要做的事情

  onPauseOrDispose {
    // onPause需要做的事
  }
}

需要注意,这两个副作用函数是针对事件对的,也就是说必须要带着后面的onStopOrDispose和onPauseOrDispose。如果仅对onStart感兴趣,而无须在onStop中做清理,那么应该直接使用LifecycleEventEffect(Lifecycle.Event.ON_START) {}(对于onResume也是同理)。

扩展阅读:

  • Managing Lifecycles Events on Jetpack Compose
  • Integrate Lifecycle with Compose

监听生命周期事件

除了直接使用生命周期副作用函数以外,也可以用lifecycle原生的方式,直接向LifecycleOwner注册一个LifecycleEventObserver来监听生命周期。通过Compose提供的LifeCycleOwner.current可以获得当前的LifecycleOwner,然后向其注册一个LifecycleEventObserver,当平台生命周期发生变化时,就会带着事件类型回调给监听者,监听者可以针对感兴趣的事件做操作。还需要注意的是,需要在组合结束(离开)时反注册observer,因此这里要用DisposableEffect。对于副作用函数不熟悉的同学可以去复习一下降Compose十八掌之『龙战于野』| Side Effects。

来看个简单的示例:

val lifecycleOwner = LocalLifecycleOwner.current

    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_CREATE -> { /* onCreate */ }
                Lifecycle.Event.ON_START -> { /* onStart */ }
                Lifecycle.Event.ON_RESUME -> { /* onResume */ }
                Lifecycle.Event.ON_PAUSE -> { /* onPause */ }
                Lifecycle.Event.ON_STOP -> { /* onStop */ }
                Lifecycle.Event.ON_DESTROY -> { /* onDestroy */ }
                Lifecycle.Event.ON_ANY -> { /* Any event */ }
                else -> {}
            }
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

这样就可以监听到生命周期事件,然后针对不同的事件做相应的操作。

当然,如果事件不止做一件事情,或者说对事件的响应不光光是执行一些函数,可能还会有页面的修改,那么这时最好就是把事件保存为一个状态(State),更为方便:

    val lifecycleOwner = LocalLifecycleOwner.current

    var lifecycleEvent by remember { mutableStateOf(Lifecycle.Event.ON_ANY) }

    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            lifecycleEvent = event
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

    LaunchedEffect(lifecycleEvent) {
        if (lifecycleEvent == Lifecycle.Event.ON_RESUME) {
            viewModel.refresh()
        }
    }

    Column() {
        if (lifecycleEvent == Lifecycle.Event.ON_RESUME) {
            Text("Welcome back")
        }
    }

扩展阅读:

  • Android handle lifecycle event on Jetpack Compose Screen
  • Jetpack Compose — Making Composable lifecycle-aware
  • Jetpack Compose with Lifecycle-Aware Composables

以数据流的方式来处理生命周期事件

生命周期是由系统控制,不时发生变化,每次变化会向监听者回调一个事件,如果以一定的时间跨度来看待,这些事件就形成了一个数据流。因此,Lifecycle还提供了一个Flow接口,用以发送Lifecycle事件。可以当作状态(State)来收集此Flow,这样事件的变化就能驱动Compose的重组,进而感知到最新的生命周期事件:

val lifecycleOwner = LocalLifecycleOwner.current
val stateFlow = lifecycleOwner.lifecycle.currentStateFlow
val currentLifecycleState by stateFlow.collectAsState()

// 或者
val lifecycleOwner = LocalLifecycleOwner.current
val currentLifecycleState = lifecycleOwner.lifecycle.currentStateAsState()

注意: 对于Flow不熟悉的同学可以复习一下包教包会的Kotlin Flow教程。

不要在ViewModel中感知生命周期

根据现代安卓开发架构原则,ViewModel应该处理与UI相关的业务逻辑,它应该独立于平台,因此,千万不要在ViewModel去感知生命周期,事实上你也做不到,因为ViewModel是没任何对平台的依赖的,非常独立的一个类型,也即拿不到LifecycleOwner。

当然了,有同学说,我可以从Compose的Composable中把LifecycleOwner当作参数传给ViewModel,但仍然强烈不建议这样做。深层的原因在于,ViewModel是独立于平台的,它有自己的生命周周期,平台的组件(如Activity)是由系统控制的,但ViewModel是由我们自己控制的,它的生命周期要长于平台的组件,也就是说ViewModel的生命周期要长于它持有的LifecycleOwner,故LifecycleOwner可能会变得过时(非当前的Activity了),同时因为被更长生命的ViewModel持有,原LifecycleOwner可能无法被回收而引发内存泄漏。

ViewModel只应该负责业务逻辑相关的事情,在Composable中监听生命周期事件很方便,也很合适,然后调用ViewModel的相应的接口(如refresh())即可。

总结

得益于Jetpack中的Lifecycle组件,在Compose中感知生命周期没有想像中的那样难。在实际项目中,推荐使用更符合Compose的方式,也即生命周期副作用函数以及事件数据流。如果仅是在某些生命周期事件发生时执行一些操作,那就用LifecycleEventEffect函数;如果不止一处需要使用事件,那就用事件数据流。

subscription

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

保护原创,请勿转载!

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

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

相关文章

Mail PHP: 如何设置SMTP服务器以发送邮件?

Mail PHP的功能怎么有效配置?Mail PHP的性能如何优化? 无论是用户注册确认、密码重置,还是系统通知,邮件发送功能都是不可或缺的。在PHP中,使用SMTP服务器发送邮件是一种高效且可靠的方式。AokSend将详细介绍如何在Ma…

如何通过HARQ确定新传和重传?

有朋友对如何通过HARQ判断是否是新传还是重传有疑问,这里就简单整理下相关内容。 先看下DL 新传和重传是如何判定的。 HARQ process根据DCI 中的New data indicator(NDI) field指示来判定接收的TB块是新传还是重传block: 1 相同HARQ id ,相比…

【C++ 面试 - STL】每日 3 题(九)

✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…

开学季有什么必备好物?这篇好物推荐不要错过!

随着开学季节的来临,众多商家纷纷推出一系列的优惠活动,这使得开学季成为了购物的理想时机。无论是电子产品还是日常用品,此时购买都非常合算。下面,我将为大家推荐几款开学季不可或缺的好物。开学季有什么必备好物?如…

AI艺术创作福利:免费领取红包封面,Meo喵、龙小金与你共庆佳节!

🎉🐉🐱 亲爱的朋友们,佳节将至,北京时间24年9月6日18:00,我们通过Midjourney的AI艺术创作和ComfyUI设计,特别为大家准备了一份特别的礼物——1588个独家设计的微信红包封面!欢迎关注…

2024年全国大学生数学建模比赛思路、题目、代码

竞赛时间及参赛建议 竞赛开始时间:北京时间2024年9月5日18:00 竞赛结束时间:北京时间2024年9月8日20:00 关于今年每道题的思路,可以关注我gzh回复”国赛A/B/C/D/E题“获取 需要帮助的可以关注公众号,在功能栏点击联系我们&…

利用大模型实时提取和检索多模态数据探索-利用 Indexify 进行文档分析

概览 传统的文本提取方法常常无法理解非结构化内容,因此提取数据的数据往往是错误的。本文将探讨使用 Indexify,一个用于实时多模态数据提取的开源框架,来更好地分析pdf等非结构化文件。我将介绍如何设置 Indexify,包括服务器设置…

智能设计#生成式海报

终于有机会把智能海报,做个升级了。比几年前的做法优雅了很多,也没有了素材有限的困扰。1 点一次,生成4张图2 选1张图,点海报生成3 得到封面海报

vue----一维数组根据同一id改成二维数组

vue----一维数组根据同一id改成二维数组 初始数据(多个dimension_id值一样): 转换后的数据(类似于): [{dimension_id: xxxxxxxxx,desc: 111,res: [{ id: 4444444, self_score: 90 },{ id: 5555555, sel…

PulseSensor心率传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.接线图 2.引脚描述 3.工作原理:光电容积法原理 4.工作原理:心率采样数据处理算法 三、程序设计 main.c文件 adcx.h文件 adc.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 PulseSensor传感器是一种基…

4.1 溪降技术:峡谷等级规划

Content 4.1 溪降技术:峡谷等级规划概述观看视频课程电子书:峡谷等级评定FFME(法国)系统FFME等级评定系统 - 工作原理垂直特征或“V”等级水特征或“A”等级难度/持续时间 ACA(美国)等级评定系统ACA等级评定…

啊!FLUX 模型爆火,电商人爱死!好用快冲!

今天,我来分享一批FLUX模型搭配使用的LORA,可能有些朋友不太了解FLUX模型,这里也做快速做个简要介绍:FLUX模型是在2024年8月1日发布的。这款模型出自Black Forest Labs之手,其团队正是Stable Diffusion的原班人马&…

PCI Express 体系结构导读摘录(三)

系列文章目录 PCI Express 体系结构导读摘录(一) PCI Express 体系结构导读摘录(二) PCI Express 体系结构导读摘录(三) 文章目录 系列文章目录第 6 章  PCIe 总线的事务层6. 1  TLP 的格式6. 1. 1 通用 …

【运维自动化-作业平台】如何快速执行脚本和分发文件

脚本执行和文件分发是作业平台最基本、最核心的两个原子功能,主要分页面快速执行和作业里步骤引用,使用逻辑一样,一起来看看具体如何使用快速执行脚本 核心实现原理就是基于gse的命令管道,把脚本内容以WebPortal的方式透传到目标…

基于yolov8的口罩佩戴检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的口罩佩戴检测系统是一款利用深度学习技术,特别是YOLOv8算法,实现高效、准确检测人脸是否佩戴口罩的系统。YOLOv8作为YOLO系列算法的最新版本,在检测速度和准确性上进行了显著优化,能够实时处理图像和视…

UDP协议程序设计

文章目录 前言一、UDP程序设计是什么?二、使用步骤 1.数据包套接字与多播套接字2.数据报包3.实操展示总结 前言 UDP协议程序相对于TCP协议,就是一个广播喇叭给全村人听和两个人说悄悄话的差别。因此UDP的数据传输效率比TCP高,可以同时分享给所…

《Windows PE》3.1 基本概念

在正式讲解PE文件格式之前,我们有必要先熟悉和PE相关的一些基本概念,以便于更好的理解和掌握PE文件格式。 本节必须掌握的知识点: 地址 指针 数据目录项 节 对齐方式 字符串编码格式 3.1.1 地址 ■在PE文件中涉及到四类地址 ●VA虚拟内存地…

【Linux】Shell 与权限:Linux 系统的双重保障

欢迎来到 CILMY23 的博客 🏆本篇主题为:Shell 与权限:Linux 系统的双重保障 🏆个人主页:CILMY23-CSDN博客 🏆系列专栏:Python | C | C语言 | 数据结构与算法 | 贪心算法 | Linux | 算法专题 …

三、搭建网站服务器超详细步骤——FinalShell下载安装使用流程(免费国产的SSH工具)+宝塔安装方法(分享两种安装宝塔的方法)

前言 本篇博客是搭建网站服务器模块下的第3部分 FinalShell下载安装使用流程 在分享这篇博客之前,首先讲一下,FinalShell软件是干什么用的,用大白话进行说明一下:这个软件是一款远程控制和管理服务器的软件,通过S…

C++字符串与整数的相互转换

文章目录 前言字符串转整数stoiisstringstreamatoi 字符转整数to_stringstringstreamsprintf ASCII码转换 前言 题目大致为: 给一组数据,去掉里面的2,然后再返回结果 例如: 输入:{20, 66, 521, 2024} 输出&#xff1…