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


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: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}
常见可变类型

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

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

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

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

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

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-03-12 07:00:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-12 07:00:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-12 07:00:06       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-12 07:00:06       20 阅读

热门阅读

  1. 公式排序算法实际运用

    2024-03-12 07:00:06       20 阅读
  2. 每天学习一个Linux命令之less

    2024-03-12 07:00:06       22 阅读
  3. 二、UML 类图与面向对象设计原则 之 UML概述

    2024-03-12 07:00:06       25 阅读
  4. excel 将缺失的单元个填充为NA

    2024-03-12 07:00:06       34 阅读
  5. [2023年]-hadoop面试真题(三)

    2024-03-12 07:00:06       18 阅读
  6. Unity 3D常用的数据结构

    2024-03-12 07:00:06       22 阅读
  7. 大语言模型提示工程简介

    2024-03-12 07:00:06       22 阅读
  8. 二维的旋转平移矩阵

    2024-03-12 07:00:06       17 阅读
  9. 矩阵最大权值

    2024-03-12 07:00:06       22 阅读
  10. 华为HCIE实验题库哪里有?Cloud相关证书咋样?

    2024-03-12 07:00:06       23 阅读
  11. 【DevOps基础篇】Dockerfile快速掌握

    2024-03-12 07:00:06       20 阅读