Compose 生命周期和副作用

news2024/11/23 22:00:51

文章目录

  • Compose 生命周期和副作用
    • 生命周期
    • 副作用API
      • DisposableEffect
      • SIdeEffect
      • LaunchedEffect
      • rememberCoroutineScope
      • rememberUpdatedState
      • snapshotFlow
      • produceState
      • derivedStateOf

Compose 生命周期和副作用

生命周期

在这里插入图片描述

  • OnActive:添加到视图树。即Composable被首次执行,在视图树上创建对应的节点。
  • OnUpdate:重组。Composable跟随重组不断执行,更新视图树上的对应节点。
  • OnDispose:从视图树移除。Composable不再被执行,对应节点从视图树上移除。

Composable在角色上更加类似于传统视图的View,所以没有Activity或者Fragment那样的前后台切换的概念,生命周期相对简单。

副作用API

副作用(Side-Effects):指在 Composable 函数中执行的可能会产生外部影响或依赖于外部资源的操作。如,访问数据库、网络请求、文件操作、弹出Toast、保存本地文件、访问远程或本地数据等。

重组可能会造成Composable频繁反复执行,副作用不应该跟随重组反复执行的。

为了处理副作用,Android Compose 提供了一系列副作用API。

DisposableEffect

DisposableEffect 可以感知 Composable 的 onActive 和 onDispose,允许通过副作用完成一些预处理和收尾处理。

@Composable
fun LifecyclePage() {
    var counter by remember { mutableStateOf(0) }
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("Counter: ${counter}")
        Row {
            Button(onClick = { counter-- }) {
                Text("-")
            }
            Button(onClick = { counter++ }) {
                Text("+")
            }
        }
    }

    DisposableEffect(Unit) {
        Log.e("TAG", "预处理")
        counter = 100

        onDispose {
            Log.e("TAG", "销毁处理")
        }
    }
}

SIdeEffect

SIdeEffect 会在重组成功时执行,不适合处理那些耗时或者异步的副作用逻辑。

@Composable
fun LifecyclePage() {
    var counter by remember { mutableStateOf(0) }
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("Counter: ${counter}")
        Row {
            Button(onClick = { counter-- }) {
                Text("-")
            }
            Button(onClick = { counter++ }) {
                Text("+")
            }
        }
    }

    SideEffect {
        Log.e("TAG", "hello 重组了")
    }
}

LaunchedEffect

用于异步操作。

当 Composable 进入 OnActive 时,LaunchedEffect 会启动协程执行 block 中的内容,可以在其中启动子协程或者调用挂起函数。当Composable 进入 OnDispose 时,协程会自动取消,因此 LaunchedEffect 不需要实现OnDispose{…}。

@Composable
fun LifecyclePage() {
    var counter by remember { mutableStateOf(0) }
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("Counter: ${counter}")
        Row {
            Button(onClick = { counter-- }) {
                Text("-")
            }
            Button(onClick = { counter++ }) {
                Text("+")
            }
        }
    }
    
    LaunchedEffect(Unit) {
        async {
            delay(2000L)
            Log.e("TAG", "异步处理")
            counter = 2000
        }
    }
}

rememberCoroutineScope

协程操作。

LaunchedEffect 虽然可以启动协程,但是只能在 Composable 中调用。如果想在非 Composable 中使用协程,如点击事件中,可以使用 rememberCoroutineScope。

rememberCoroutineScope 会返回一个 CoroutineScope,并在 OnDispose 时自动取消。

@Composable
fun LifecyclePage() {
    var counter by remember { mutableStateOf(0) }
    val scope = rememberCoroutineScope()

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("Counter: ${counter}")      
        Button(onClick = {
            scope.launch {
                Log.e("TAG", "协程操作")
                counter = 3000
            }
        }) {
            Text("协程操作")
        }
    } 
}

rememberUpdatedState

监听状态最新状态。

rememberUpdatedState的实现就明白其中原理了,其实就是remember和mutableStateOf的组合使用

@Composable
fun LifecyclePage() {
    var counter by remember { mutableStateOf(0) }
    val updateCounter by rememberUpdatedState(newValue = counter)

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text("Counter: ${counter}")
        Row {
            Button(onClick = { counter-- }) {
                Text("-")
            }
            Button(onClick = { counter++ }) {
                Text("+")
            }
        } 
        Text("监听最新状态:${updateCounter}")
    }
}

snapshotFlow

snapshotFlow 可以把 Compose 的 State 状态对象转成协程的 Flow。

@Composable
fun LifecyclePage() {
    var time by remember { mutableStateOf("${System.currentTimeMillis()}") }
    val flow = snapshotFlow { time }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Button(onClick = {
            time = "${System.currentTimeMillis()}"
        }) {
            Text("更新")
        }
    }

    LaunchedEffect(Unit) {
        flow.collect {
            Log.e("TAG", "snapshotFlow: ${it}")
        }
    }
}

produceState

SideEffect 常用来将Compose的State暴露给外部使用,而produceState则相反,可以将一个外部的数据源转成State。外部数据源可以是一个LiveData或者RxJava这样的可观察数据,也可以是任意普通的数据类型。

data class Person(val name: String, val age: Int, val time: Long)

@Composable
fun MyProduceState() {
    var name by remember { mutableStateOf("小明") }
    var age by remember { mutableStateOf(18) }

    val personState by produceState(
        initialValue = Person(name, age, System.currentTimeMillis()),
        name,
        age
    ) {
        value = Person(name, age, System.currentTimeMillis())
    }
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Button(onClick = { name = "小红" }) {
            Text("修改name")
        }
        Button(onClick = { age = 19 }) {
            Text("修改age")
        }
        Text("produceState: ${personState}")
    }
}

说明:修改 name 或 age 都会触发 produceState。

derivedStateOf

derivedStateOf用来将一个或多个State转成另一个State。derivedStateOf{…}的block中可以依赖其他State创建并返回一个DerivedState,当block中依赖的State发生变化时,会更新此DerivedState,依赖此DerivedState的所有Composable会因其变化而重组。

@Composable
fun MyDerivedStateOf() {
    var postList by remember { mutableStateOf(listOf("a", "b", "c", "d", "e")) }
    var keyword by remember { mutableStateOf("") }
    val result by remember {
        derivedStateOf { postList.filter { it.contains(keyword, false) } }
    }
    Text("过滤:${result}")
    Button(onClick = { postList = listOf("1", "2", "3", "4", "5") }) {
        Text("修改List")
    }
    OutlinedTextField(value = keyword, onValueChange = { keyword = it }, label = { Text("请输入") })
}

说明:当 postList 或 keyword 方式变化时,derivedStateOf 都会重新计算,result 也就更新了。

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

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

相关文章

传闻不断!TCL紧急澄清 | 百能云芯

TCL科技5月7日晚间发布澄清公告称,近日关注到有媒体发布《TCL华星年内投630亿元加入8代oled线竞逐!》《TCL华星计划年内投资第八代OLED》等相关报道。公司目前无新建8代或8.6代OLED产线的投资计划,公司不存在通过定增募集资金新建显示产线的计…

期权和期货有什么区别?

今天期权懂带你了解期权和期货有什么区别?期权和期货是两种常见的衍生金融工具,它们在结构和盈利方式上存在一些关键的区别: 期权 期权是一种给予持有者在未来某个时间以特定价格买入或卖出基础资产的权利,但不是义务。期权的主要…

直播报名 | 珈和科技携手潍柴雷沃共探“现代农场”未来式

数据赋农季系列直播第四期,我们将以“未来农业发展趋势之农场智慧化、管理数据化”为主题展开,此次系列直播由珈和科技及湖北珞珈实验室共同主办,第四期直播很荣幸邀请到潍柴雷沃参与其中,双方将就智慧农服平台和数据交易SaaS平台…

C#里如何设置输出路径,不要net7.0-windows

官网介绍&#xff1a; 更改生成输出目录 - Visual Studio (Windows) | Microsoft Learn <PropertyGroup> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendRuntimeIdentifierToOutputPath>false</Appen…

RabbitMQ基础入门

初识MQ 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但是你却不能跟多个人同…

Python自动化下载指定公开页面文件

示例代码如下&#xff0c;但你拿到本地之需要做两件事才能运行 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time# 设置Se…

【电路笔记】-Twin-T振荡器

Twin-T振荡器 文章目录 Twin-T振荡器1、概述2、Twin-T振荡器3、Twin-T放大4、Twin-T 振荡器示例5、总结Twin-T 振荡器是另一种 RC 振荡器电路,它使用两个并联的 RC 网络来产生单一频率的正弦输出波形。 1、概述 Twin-T 振荡器是另一种类型的 RC 振荡器,它产生正弦波输出,用…

已经安装tensorflow,仍报错No module named ‘tensorflow‘

在安装某些python虚拟环境的教程文章中&#xff0c;经常看到有评论区说安装了但是调用显示无模块&#xff0c;例如pytorch和tensorflow等等。 其实跟之前我写过的一篇文章解决方法类似&#xff0c;就是python项目中需要应用哪个虚拟环境&#xff0c;这个项目的python解释器就选…

LCD驱动IC-抗干扰液晶段码显示屏驱动芯片,液晶显示驱动原厂-VK2C23A/B LQFP64/48

产品品牌&#xff1a;永嘉微电/VINKA 产品型号&#xff1a;VK2C23A/B 封装形式&#xff1a;LQFP64/48 概述 VK2C23是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大224点&#xff08;56SEGx4COM&#xff09; 或者最大416点&#xff08;52SEGx8COM&#xff09;的LCD屏。…

API开发的必备神器:华为云CodeArts API实用体验入门篇

今天我想给大家推荐一款API全生命周期研发与管理工具&#xff1a;华为云CodeArts API。 作为互联网软件的开发者&#xff0c;在软件研发的过程中&#xff0c;API的开发、调试、测试是必不可少的。之前我使用的是Postman这类工具来辅助开发&#xff0c; Postman在接口调试方面确…

第 8 章 电机调速(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 8.3.5 电机调速01_PID控制理论 场景&#xff1a; 速度信息可以以m/s为单位&#xff0c;或者也可以转换成转速 …

第18讲:Ceph集群CrushMap的核心概念、默认规则与完整定义

文章目录 1.CrushMap核心概念2.集群默认的CrushMap规则剖析2.1.CrushMap列表显示内容剖析2.2.对默认的CrushMap规则进行深度的剖析2.3.完整的CrushMap定义信息 1.CrushMap核心概念 CrushMap官方文档&#xff1a;https://docs.ceph.com/en/pacific/rados/operations/crush-map/…

【Alluxio】文件系统锁模型之InodeLockList

InodeLockList接口,表示在inode tree里一个加了锁的路径。 沿着path,inodes和edges都被加锁了。path可能从edge或inode任意一个开始。 锁列表总是包含了一定数量的读锁(0个或多个),随后跟随着一些数量的写锁(0个或多个)。 举个例子: 对 /a/b/c/d 进行加锁,c->d这…

QGraphicsItem的prepareGeometryChange 和 update方法区别

prepareGeometryChange 这个函数用于为图形的几何形状变化做准备。在改变一个项目的边界矩形之前调用此函数&#xff0c;以保持 QGraphicsScene 的索引是最新的。如果必要的话&#xff0c;prepareGeometryChange() 会调用 update()。QGraphicsScene认为所有图元的boundingRect…

ReactFlow的ReactFlow实例事件传参undefined处理状态切换

1.问题 ReactFlow的ReactFlow实例有些事件我们在不同的状态下并不需要&#xff0c;而且有时候传参会出现其它渲染效果&#xff0c;比如只读状态下我们不想要拖拉拽onEdgesChange连线重连或删除的功能。 2.思路 事件名称类型默认值onEdgesChange(changes: EdgeChange[]) >…

.NET邮箱API发送邮件的步骤?怎么配置API?

.NET邮箱API发送邮件需要注意哪些&#xff1f;如何使用API发信&#xff1f; 在.NET环境中&#xff0c;使用邮箱API发送邮件是一个常见的需求。无论是企业级的邮件通知&#xff0c;还是个人项目中的邮件验证&#xff0c;都少不了.NET邮箱API的帮助。下面&#xff0c;AokSend将详…

MT3033 新的表达式

代码&#xff1a; #include <bits/stdc.h> using namespace std; bool is_op(char c) {return c & || c |; } int priority(char op) { // 运算优先级。如果有-*/等别的运算符&#xff0c;则这个函数很有必要if (op & || op |){return 1;}return -1; } voi…

内网渗透(二)

预备知识 什么是域&#xff1f; 域是若干台计算机组成的集合&#xff0c;一个电脑也是。域中的电脑是分等级的&#xff0c;分为域控和成员机。 如何安装域&#xff1f; 在服务器管理中添加服务器角色&#xff0c;添加域服务 如何加入域? 首先一定要修改DNS服务器 ip为域…

Davinci工程CAN模块讲解

CAN模块是用来配置CAN Driver的&#xff0c;里面有CanConfigSet是用来配置驱动内容的&#xff0c;CanGeneral配置参数。涉及四个文件Can_Lcfg.c/Can_Lcfg.h/Can_Cfg.c/Can_Cfg.h CanConfigSet CanControllers CAN控制器&#xff0c;我们这里的CAN控制器只有一个&#xff0c;名…

Gradle报错Cause: zip END header not found,构建问题解决

问题描述 构建报错&#xff1a;Cause: zip END header not found 解决办法 File>>setting>>Build,Execution,Deployment>>Gradle 选择你本地的Gradke路径 问题解决