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

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

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

Composable生命周期简介

从官方文档我们可以知道,Compsable生命周期分为:

1、添加到视图树:将Composable添加到组合中,首次执行,在视图树上新增节点

2、重组:不断重组,更新视图树的界面

3、从视图树移除:将Composable从组合中移除

重组

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

示例

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

kotlin 复制代码
@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源码如下

kotlin 复制代码
@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后的打印结果
kotlin 复制代码
@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的变化是可以被记录追踪并触发重组的(等同于在新重组发生之前保持不变)

kotlin 复制代码
@Stable
interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}
常见可变类型

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

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

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

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

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

相关推荐
Devil枫35 分钟前
Kotlin高级特性深度解析
android·开发语言·kotlin
ChinaDragonDreamer37 分钟前
Kotlin:2.1.20 的新特性
android·开发语言·kotlin
雨白11 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹13 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空14 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭15 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日16 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安16 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑16 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟20 小时前
CTF Web的数组巧用
android