Compose基础-SideEffect(二)
1. 前言
在Compose基础-Side-effect(一)中,我们学习了几个常用的Side-effect:LaunchedEffect和rememberCoroutineScope,以及关键字rememberUpdateState的用法。在本篇文章中,我们将介绍剩下几个常用的Side-effect相关关键字的用法。
2. DisposableEffect
对于有些Side-effect,在其key值变化或者composable函数离开Composition时,需要对一些资源等进行清理。这种情况下,可以使用谷歌提供的DisposableEffect。其特点如下:
当DisposableEffect的key值变化时,当前Effect的onDispose会被调用,此时可以在此函数中对资源进行清理;同时DisposableEffect会被重启,此时可以重新申请资源等。
DisposableEffect中必须包含onDispose语句,否则IDE会出现编译时错误。
以下是使用DisposableEffect的一个示例,该DisposableEffect为LifecycleOwner注册了回调,用于监听一些生命周期事件便于数据统计。代码如下:
@Composable
fun HomeScreen(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,onStart: () -> Unit, // Send the 'started' analytics eventonStop: () -> Unit // Send the 'stopped' analytics event
) {// Safely update the current lambdas when a new one is providedval currentOnStart by rememberUpdatedState(onStart)val currentOnStop by rememberUpdatedState(onStop)// If `lifecycleOwner` changes, dispose and reset the effectDisposableEffect(lifecycleOwner) {// Create an observer that triggers our remembered callbacks// for sending analytics eventsval observer = LifecycleEventObserver { _, event ->if (event == Lifecycle.Event.ON_START) {currentOnStart()} else if (event == Lifecycle.Event.ON_STOP) {currentOnStop()}}// Add the observer to the lifecyclelifecycleOwner.lifecycle.addObserver(observer)// When the effect leaves the Composition, remove the observeronDispose {lifecycleOwner.lifecycle.removeObserver(observer)}}/* Home screen content */
}
DisposableEffect常用于抽取一些需要在composable函数离开Composition或者key值变化时清理资源的场景,例如监听的反注册。
3. SideEffect
有时候,我们需要将Compose状态共享给非Compose管理的对象。此时,Google建议我们使用SideEffect函数,因为每次recompose成功时都会调用该函数。SideEffect特点如下:
在Composition操作失败时,能保证SideEffect中的非Compose管理对象的状态和Composition中的状态一致。
SideEffect在LayoutNode被创建并插入layout tree后才会被调用。
以下是使用SideEffect的一个示例。分析库允许通过将自定义元数据(在此示例中为“用户属性”)附加到所有后续分析事件,来细分用户群体。如需将当前用户的用户类型传递给分析库,需要使用 SideEffect 更新其值。
@Composable
fun rememberAnalytics(user: User): FirebaseAnalytics {val analytics: FirebaseAnalytics = remember {/* ... */}// On every successful composition, update FirebaseAnalytics with// the userType from the current User, ensuring that future analytics// events have this metadata attachedSideEffect {analytics.setUserProperty("userType", user.userType)}return analytics
}
4. produceState
有时候我们会使用协程来获取数据,例如Flow,但是该数据是在Composition中使用。为了使用这些数据,我们需要使用produceState来将non-Compose state来转变为Compose state。produceState特点如下:
当produceState进入Composition时,获取数据的任务被启动,当其离开Composition时,该任务被取消。
尽管produceState创建了一个协程,它也可以用于获取non-suspending数据源。
以下示例展示了如何利用produceState从网络加载一张图片。loadNetworkImage这个函数返回了一个State,这个State可以被用于其他composable函数。
@Composable
fun loadNetworkImage(url: String,imageRepository: ImageRepository
): State<Result<Image>> {// Creates a State<T> with Result.Loading as initial value// If either `url` or `imageRepository` changes, the running producer// will cancel and will be re-launched with the new inputs.return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) {// In a coroutine, can make suspend callsval image = imageRepository.load(url)// Update State with either an Error or Success result.// This will trigger a recomposition where this State is readvalue = if (image == null) {Result.Error} else {Result.Success(image)}}
}
5. derivedStateOf
当一个状态由另外几个状态计算或者推导得到时,使用derivedStateOf来记录结果状态,此时作为条件的状态我们称为条件状态。当任意一个条件状态更新时,结果状态都会重新计算。以下是一个示例。
@Composable
fun DevitedStateCompose() {val input1: MutableState<String> = remember{mutableStateOf("")}val input2: MutableState<String> = remember{mutableStateOf("")}val result1: State<String>= derivedStateOf{"${input1.value} + ${input2.value}"}Column{ContentWrap{Column{TextField(value = input1.value,onValueChange = {input1.value = it })TextField(value = input2.value,onValueChange = {input2.value = it })Log.d("DevitedStateTest", "recompose Column")}}ContentWrap{Text(result1.value)Log.d("DevitedStateTest", "ContentWrap button")}}Log.d("DevitedStateTest", "recompose DevitedStateCompose")
}@Composable
fun ContentWrap(content: @Composable () -> Unit) {content()
}
当input1或者input2有变化时,result1会被重新计算。此时ContentWrap button会被打印,但是recompose DevitedStateCompose不会被打印。即展示结果的ContentWrap被recompose,但是DevitedStateCompose函数未recompose。
6. snapshotFlow
由于Flow强大的数据操作能力,有时候我们想将State转化为Flow,此时我们需要使用snapshotFlow来将State转化为Flow。snapshotFlow的特点如下:
- snapshotFlow中读取的State发生变化时,snapshotFlow会重新生成Flow。
以下是一个示例。
val listState = rememberLazyListState()LazyColumn(state = listState) {// ...
}LaunchedEffect(listState) {snapshotFlow { listState.firstVisibleItemIndex }.map { index -> index > 0 }.distinctUntilChanged().filter { it == true }.collect {MyAnalyticsService.sendScrolledPastFirstItemEvent()}
}
在上述代码中,如果firstVisibleItemIndex发生了变化,那snapshotFlow会将listState.firstVisibleItemIndex转化为Flow。
7. 小结
本文主要介绍了DisposableEffect,SideEffect,produceState,derivedStateOf,snapshotFlow五种Side-effect的作用和使用场景。其重点如下:
DisposableEffect用于在离开composition时清理一些资源。
SideEffect用于将Compose状态共享给非Compose管理的对象。
produceState用于将non-Compose state来转变为Compose state。
derivedStateOf用于记录几个状态计算或者推导得到的状态。
snapshotFlow用于将State转化为Flow。
8. 参考文档
Side-effects in Compose
Tricky refactoring of Jetpack Compose code — be careful with side effects
Compose基础-SideEffect(二)相关推荐
- 【C++自我精讲】基础系列二 const
[C++自我精讲]基础系列二 const 0 前言 分三部分:const用法.const和#define比较.const作用. 1 const用法 const常量:const可以用来定义常量,不可改变 ...
- java负数右移_收入囊中篇---Java程序基础(二)
前言: 本篇是接着上一篇更新的,如果没有阅读上一篇的话,可以查阅或回顾一下. 1.收入囊中篇---Java基础必备知识(一) 2.收入囊中篇---Java程序基础(二) Java程序基础目录 1.Ja ...
- mysql 基础篇(二) 账号、权限管理
mysql 基础篇(二) 账号.权限管理.备份与还原 建立账号密码: Grant all on test.* to "cj"@"localhost" ident ...
- JVM 内部原理(七)— Java 字节码基础之二
JVM 内部原理(七)- Java 字节码基础之二 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...
- CV:计算机视觉技术之图像基础知识(二)—图像内核的可视化解释
CV:计算机视觉技术之图像基础知识(二)-图像内核的可视化解释 目录 图像内核的可视化解释 测试九种卷积核 官方Demo DIY图片测试 DIY实时视频测试 相关文章 CV:计算机视觉技术之图像基础知 ...
- CV:计算机视觉技术之图像基础知识(二)—以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础)
CV:计算机视觉技术之图像基础知识(二)-以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础) 目录 ...
- MySQL基础总结(二)
MySQL基础总结(二) 文章目录 MySQL基础总结(二) 四.索引 7.MyISAM主键索引与辅助索引的结构 8.InnoDB主键索引与辅助索引的结构 **`主键索引`** **`辅助(非主键)索 ...
- 网络基础(二)及HTTP协议
网络基础(二)及HTTP协议 文章目录 网络基础(二)及HTTP协议 一.HTTP协议 二.端口 三.udp协议 四.tcp协议 一.HTTP协议 1 . 什么是url? 平时我们俗称的 " ...
- 计算机应用基础第二版在线作业c,计算机应用基础作业二(答案)
计算机应用基础作业二 一.单选题(40题,每题1分,共40分) 1.第一台电子数字计算机的运算速度为每秒______. A:5,000,000次 B:500,000次 C:50,000次 D:5000 ...
- (五)JS基础知识二(通过图理解原型和原型链)【三座大山之一,必考!!!】
JS基础知识二(原型和原型链) 提问 class 继承 类型判断(instanceof) 原型 原型关系 基于原型的执行规则 原型链 说明 提问 如何准确判断一个变量是不是数组 class的原型本质 ...
最新文章
- 前端html5的框架有哪些,10大html5前端框架
- java面向对象三大特性:封装、继承、多态——举例说明
- 【粉丝需求】如何把一个前端网页都搞下来?
- bzoj2287【POJ Challenge】消失之物 缺一01背包
- Shell编程之变量
- Linux LVM动态扩容
- java 字符串和整型的相互转换
- 【android自定义控件】button样式自定义二
- 微信小程序实现上传图片的功能
- 【嵌入式C语言系列】关键字详解【const】
- 区块链安全入门与实战
- 基于Android和Java的校园外卖系统设计与实现
- linux 文件名带日期,在linux中追加日期到文件名
- RocketMQ可视化Web管理界面
- Android 应用开发入门
- 成熟港口人工智能Ceaspectus领跑全球智能港口码头人工智能应用落地,全球No.1集装箱AI企业中集飞瞳建设智慧港口智能码头
- 二进制与8,10,16转换
- 内存调试神器- ASan详解及实例分析
- 安卓6.0+关机状态下通电自动开机方案
- 工商管理专业考计算机二级,全国计算机二级科目怎么选
热门文章
- 嵌入式分享合集126
- 用Python做一个游戏辅助脚本,完整编程思路分享
- TCP/IP Attack Lab(SEED实验)
- 新手网站制作教程:网站建设流程及步骤有哪些?
- linux 硬盘合并使用方法,Linux硬盘合并的实现代码
- java中display中的属性_全面解析display属性
- 洛谷 P3604 美好的每一天
- 小米手机销量超过苹果晋升全球第二
- 大气科学领域必备的模型软件汇总丨WRF、WRF-CMAQ、WRF-Chem、WRF-Hydro、WRF DA、PMF、MCM、CAMx、SMOKE、CMIP6等
- maven dependency 警告:Overriding managed version XXX for XXX