Android Jetpack Compose基础之生命周期-重组

Android Jetpack Compose基础之生命周期-重组

  • Composable生命周期简介
  • 重组
    • Composable如何确定重组的最小范围?
      • 示例
      • 为什么结果是这样呢?其底层原理?
      • 日志顺序分析
        • 如果我们将Column替换为非内联函数Card
        • stateChangeComposable内部增加如下代码点击button后的打印结果
        • 小结
    • Composable中的参数是如何实现比较是否相同的
      • 什么是稳定类型和可变类型
        • 不可变稳定类型
        • 常见可变类型
      • @Stable:将可变类型设置为稳定类型

Compose是通过重组刷新UI,Compose发生重组时,只有状态发生更新的Composable才会参与重组,没有变化的Compsable会跳过本次重组。
首先我们简单看下Compsable的生命周期

Composable生命周期简介

在这里插入图片描述
从官方文档我们可以知道,Compsable生命周期分为:
1、添加到视图树:将Composable添加到组合中,首次执行,在视图树上新增节点
2、重组:不断重组,更新视图树的界面
3、从视图树移除:将Composable从组合中移除

重组

Composable如何确定重组的最小范围?

示例

让我们先看如下代码的日志输出

@Composable
fun stateChangeComposable() {Log.i("stateChangeComposable", "stateChangeComposable run1")var num by remember {mutableStateOf(1)}Column {Log.i("stateChangeComposable", "stateChangeComposable run2")Button(onClick = {Log.i("stateChangeComposable", "stateChangeComposable run click")num++}) {Log.i("stateChangeComposable", "stateChangeComposable run3")Text(text = "change")}Log.i("stateChangeComposable", "stateChangeComposable run4")Text(text = "current num = $num")}
}

界面中有个按钮,点击后num加1,一个文本展示num,点击Button后的日志输出顺序是什么样的呢?

实际输出内容:
请添加图片描述

为什么结果是这样呢?其底层原理?

因为Compse经过变异后,Compsable代码在对state读取的同时会自动建立关联,在运行过程中state发生改变时,Compse会将关联的代码块标记为Invalid,Compose会发重组并执行被标记为Invalid代码块。
只有非inline且无返回值的Compsable函数或者lambda才能够被标记为Invalid(1、因为inline函数在编译期会在调用处展开,所以他会共享调用方的重组范围;2、有返回值的函数,它的返回值会影响调用方,所以必须与调用方一起重组)

日志顺序分析

1、stateChangeComposable run click点击了自然会被打印
2、stateChangeComposable run2和stateChangeComposable run4,因为当前读取了num状态并传入了Text所以打印了
3、stateChangeComposable run3没有打印是因为没有内部没有依赖num状态,所以跳过了重组
4、而stateChangeComposable run1为什么会被打印呢,因为它是inline声明的函数,它内部的content会在调用处展开,所以它与调用方共享重组;Column源码如下

@Composable
inline fun Column(modifier: Modifier = Modifier,verticalArrangement: Arrangement.Vertical = Arrangement.Top,horizontalAlignment: Alignment.Horizontal = Alignment.Start,content: @Composable ColumnScope.() -> Unit
) {val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)Layout(content = { ColumnScopeInstance.content() },measurePolicy = measurePolicy,modifier = modifier)
}
如果我们将Column替换为非内联函数Card

其打印内容为下图,发现stateChangeComposable run1未出现了
在这里插入图片描述

stateChangeComposable内部增加如下代码点击button后的打印结果
@Composable
fun stateChangeComposable() {Log.i("stateChangeComposable", "stateChangeComposable run1")var num by remember {mutableStateOf(1)}{Log.i("stateChangeComposable", "stateChangeComposable run2")Button(onClick = {Log.i("stateChangeComposable", "stateChangeComposable run click")num++}) {Log.i("stateChangeComposable", "stateChangeComposable run3")Text(text = "change")}Log.i("stateChangeComposable", "stateChangeComposable run4")Text(text = "current num = $num")stateChangeComposable2 {Log.i("stateChangeComposable", "stateChangeComposable stateChangeComposable2 click")}stateChangeComposable3(num) {Log.i("stateChangeComposable", "stateChangeComposable stateChangeComposable3 click")}}
}@Composable
fun stateChangeComposable2(click: () -> Unit) {Log.i("stateChangeComposable", "stateChangeComposable stateChangeComposable2 run")Text(text = "stateChangeComposable2",modifier = Modifier.clickable { click() })
}@Composable
fun stateChangeComposable3(num: Int, click: () -> Unit) {Log.i("stateChangeComposable", "stateChangeComposable stateChangeComposable3 run")Text(text = "stateChangeComposable3 getNum = $num",modifier = Modifier.clickable { click() })
}

其日志输出结果如下
在这里插入图片描述

小结

原则:其重组依旧遵循范围最小化原则:只有收到State变化影响的代码块才会参与到重组,不依赖State的代码或参数未发生改变时则不参与本次重组。

由谁来检测参数是否发生改变:重组过程中Composable只有其参数发生变化时,才会参与到本次重组,而Composable的参数比较由编译后传入Composer对象完成的,而Composer又是和谁去比较的呢?
简单的说:它是从视图树上寻找对应位置的节点并与之进行比较,如果节点未发生变化,则不更新
详细的说:Composable执行过程,先将生成的Composition状态存入SlotTable,然后基于SlotTable生成LayoutNode树,并完成最终界面渲染,Composable的比较逻辑最终发生在SlotTable中

Composable中的参数是如何实现比较是否相同的

什么是稳定类型和可变类型

我们知道Composable是基于参数的比较结果来决定是否重组的,只有当参与比较的参数对象是不可变的稳定的且equals返回true,才认为是未发生改变是相等的。

不可变稳定类型

在kotlin中如基本类型、String类型、函数类型(Lambda),它们都是不可变类型,所以它们的值可信的,否则时可变类型

补充:MutableState被认为是稳定类型,虽然它的值是可变的,但因为它的value的变化是可以被记录追踪并触发重组的(等同于在新重组发生之前保持不变)

@Stable
interface MutableState<T> : State<T> {override var value: Toperator fun component1(): Toperator fun component2(): (T) -> Unit
}
常见可变类型

1、对象中有var修饰的成员变量

class Person(
//因为他有一个var类型的成员变量age,所以它的equals的结果是不可信的var age:Int)

2、interface或者List集合也是可变类型

@Stable:将可变类型设置为稳定类型

如果我们确保可变类型在运行中是不会改变的,我们可以为其添加@Stable注解,编译期将会视为稳定类型,被添加注解的普通夫类、密封类、接口等,其子类也会被视为稳定的,可以实现避免不必要的重组

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

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

相关文章

VideoDubber时长可控的视频配音方法

本次分享由中国人民大学、微软亚洲研究院联合投稿于AAAI 2023的一篇专门为视频配音任务定制的机器翻译的工作《VideoDubber: Machine Translation with Speech-Aware Length Control for Video Dubbing》。这个工作将电影或电视节目中的原始语音翻译成目标语言。 论文地址&…

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 1、线条折线曲面

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 代码: import pandas as pd import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib.colors import ListedColo…

阿里云-零基础入门推荐系统 【Baseline】

文章目录 赛题介绍评价方式理解赛题理解代码实战导包df节省内存函数读取采样或全量数获取 用户 - 文章 - 点击时间字典获取点击最多的topk个文章itemcf的物品相似度计算itemcf 的文章推荐给每个用户根据物品的协同过滤推荐文章召回字典转换成df生成提交文件获取测试集从所有的召…

【力扣100】【好题】322.零钱兑换 || 01背包完全背包

添加链接描述 思路&#xff1a; dp[j]数组表示的是在金额达到 j 的时候所需要的最小硬币数金额&#xff1a;背包容量&#xff0c;每个硬币的个数都为1&#xff1a;背包中物品的价值&#xff0c;硬币面额&#xff1a;物品重量dp[j]min(dp[j],dp[j-coin]1) class Solution:def …

Jenkins Pipeline实现Golang项目的CI/CD

Jenkins Pipeline实现Golang项目的CI/CD 背景 最近新增了一个Golang实现的项目&#xff0c;需要接入到现有的流水线架构中。 流程图 这边流程和之前我写过的一篇《基于Jenkins实现的CI/CD方案》差不多&#xff0c;不一样的是构建现在是手动触发的&#xff0c;没有配置webho…

批量合并:一键操作,轻松将多个TXT文本合并为一个

在信息爆炸的时代&#xff0c;我们每天都面临着处理大量文本信息的挑战。无论是学习、工作还是生活中&#xff0c;TXT文件作为最基础的文本格式&#xff0c;承载着大量的重要信息。然而&#xff0c;传统的文本管理方式往往效率低下&#xff0c;难以满足我们的实际需求。那么&am…

优思学院|5S 应该由哪个部门负责推行?

很多人以为5S是生产部的事&#xff0c;负责的部门自然是生产部&#xff0c;事实上这是一个很严重的错误&#xff0c;那么&#xff0c;究竟5S 应该由哪个部门负责推行&#xff1f;我们今天就来讨论一下。如果你喜欢我们的文章&#xff0c;记得点赞和收藏。 5S是全员参与的活动&…

51单片机基础篇系列-LED灯点亮代码部分

&#x1f308;个人主页: 会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” #include<reg52.h> //包含单片机内部寄存器 void main() //&#xff08;&#xff09;{P10xfe;//1111 1110while(1); // } 上面是第一个 LED实验 #include<reg52.h>…

在 Python 中从键盘读取用户输入

文章目录 如何在 Python 中从键盘读取用户输入input 函数使用input读取键盘输入使用input读取特定类型的数据处理错误从用户输入中读取多个值 getpass 模块使用 PyInputPlus 自动执行用户输入评估总结 如何在 Python 中从键盘读取用户输入 原文《How to Read User Input From t…

vite vue3 路由配置@找不到文件问题描述

问题描述 在vite.config.js文件中配置路由的时候&#xff0c;添加路由界面&#xff0c;找不到指定的文件&#xff0c;提示错误&#xff0c;如图所示&#xff1a; 但是换成 ./ 或者 ../ 就正常了&#xff0c;也没有报错问题 解决办法 1.安装一个path的插件 npm install --sav…

《MySQL实战45讲》课程大纲

1MySQL实战45讲-01基础架构&#xff1a;一条SQL查询语句是如何执行的&#xff1f;2MySQL实战45讲-02日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f;3MySQL实战45讲-03事务隔离&#xff1a;为什么你改了我还看不见&#xff1f;4MySQL实战45讲-04深入浅出索引&…

Java后端八股文之Redis

文章目录 1. Redis是什么&#xff1f;2. Redis为什么这么快&#xff1f;3. 为什么要使用缓存&#xff1f;4. Redis几种使用场景&#xff1a;5. Redis的Zset底层为什么要使用跳表而不是平衡树、红黑树或者B树&#xff1f;6.Redis持久化6.1 什么是RDB持久化6.1.1RDB创建快照会阻塞…