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注解,编译期将会视为稳定类型,被添加注解的普通夫类、密封类、接口等,其子类也会被视为稳定的,可以实现避免不必要的重组

相关推荐
m0_548514772 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯3 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯3 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐3 小时前
Handle
android
m0_748232925 小时前
Android Https和WebView
android·网络协议·https
m0_748251725 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_748254667 小时前
go官方日志库带色彩格式化
android·开发语言·golang
zhangphil7 小时前
Android使用PorterDuffXfermode模式PorterDuff.Mode.SRC_OUT橡皮擦实现“刮刮乐”效果,Kotlin(2)
android·kotlin
爱学测试的李木子7 小时前
从0到1搭建 Android 自动化 python+appium 环境
android·软件测试·python·测试工具·自动化
咸芝麻鱼7 小时前
Android Studio | 连接手机设备后,启动App时出现:Waiting For DebuggerApplication (App名)...
android·adb·智能手机·android studio