Compose:重组机制 初探

64 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

recomposable.png 上图是官网上面的一张关于compose如何工作的图,从今天一个screen开始,根据条件的变化,compose进行0次到多次的重组。今天就看看重组相关的知识。

  • 什么情况下发生重组

其实总的一句话那就是当状态发生改变的时候发生重组,我们最多使用的就是MutableState。下面看一个简单的例子:

Column() {
    var count by remember {
        mutableStateOf(0)
    }
    Log.i("test","log count:$count")
    Text(text = "count:$count")
    Button(onClick = { count++ }) {
        Text(text = "Add")
    }
}

写了一个列表,列表里面有一个text和button,当点击button的时候text上面显示的内容会不断的发生变化,看看效果:

aa_1.gif 这里的count必须使用remember,如果不使用remember那边在发生重组的时候这个值也会被相应的重置。使用remember之后compose会记住上次更新过的值,而不会在重组的时候重新赋值。那大家想想上面的log会打印几次呢?看看运行结果:

2022-12-04 08:27:14.546 I/test: log count:1
2022-12-04 08:27:14.895 I/test: log count:2
2022-12-04 08:27:15.181 I/test: log count:3
2022-12-04 08:27:15.631 I/test: log count:4
2022-12-04 08:27:15.863 I/test: log count:5
2022-12-04 08:27:16.181 I/test: log count:6
2022-12-04 08:27:16.446 I/test: log count:7
2022-12-04 08:27:16.712 I/test: log count:8

可以看出,这个值发生了多少次变化,这个log就会打印多少次,这就说明重组了多少次。如果把上面的代码变化一下:

fun ShowText(){
    Text(text = "I'm another test")
    Log.i("test","ShowText")
}
Column() {
    var count by remember {
        mutableStateOf(0)
    }
    Log.i("test","log count:$count")
    Text(text = "count:$count")
    Button(onClick = { count++ }) {
        Text(text = "Add")
    }
    ShowText()
}

我又加了一个ShowText项,但是这个是另外一个可组合函数,那么输出结果是什么样子呢?

2022-12-04 08:38:56.251 14373-14373/com.laworks.recomposableexample I/test: log count:0
2022-12-04 08:38:56.269 14373-14373/com.laworks.recomposableexample I/test: ShowText
2022-12-04 08:38:59.396 14373-14373/com.laworks.recomposableexample I/test: log count:1
2022-12-04 08:39:00.168 14373-14373/com.laworks.recomposableexample I/test: log count:2
2022-12-04 08:39:00.919 14373-14373/com.laworks.recomposableexample I/test: log count:3
2022-12-04 08:39:01.596 14373-14373/com.laworks.recomposableexample I/test: log count:4
2022-12-04 08:39:02.929 14373-14373/com.laworks.recomposableexample I/test: log count:5

可以看出Showtext的log只打印了一次,总结一下就是Composable只会影响当前的可组合函数。 另外我们在使用动画的时候也会发生相应的重组,例如:

Column {
    var count by remember {
        mutableStateOf(0)
    }
    Log.i("test","log count:$count")
    ShowText()
    var colorChange by remember {
        mutableStateOf(false)
    }
    val colors = animateColorAsState(targetValue = if(colorChange) Color.Red else Color.Blue, animationSpec = tween(1000))
    Box(modifier = Modifier
        .background(colors.value)
        .size(100.dp))
    LaunchedEffect(key1 = Unit ){
        while (true){
            colorChange = !colorChange
            count++
            delay(2000)
        }
    }
}

打印log如下:

2022-12-04 09:02:16.752 I/test: log count:1
2022-12-04 09:02:16.780 I/test: log count:1
2022-12-04 09:02:16.799 I/test: log count:1
2022-12-04 09:02:16.813 I/test: log count:1
2022-12-04 09:02:16.832 I/test: log count:1
2022-12-04 09:02:17.346 I/test: log count:1
2022-12-04 09:02:17.362 I/test: log count:1
2022-12-04 09:02:17.746 I/test: log count:1
2022-12-04 09:02:18.762 I/test: log count:2
2022-12-04 09:02:19.762 I/test: log count:2
2022-12-04 09:02:20.762 I/test: log count:3
2022-12-04 09:02:20.828 I/test: log count:3
2022-12-04 09:02:20.845 I/test: log count:3
2022-12-04 09:02:21.762 I/test: log count:3
2022-12-04 09:02:22.764 I/test: log count:4
2022-12-04 09:02:23.765 I/test: log count:4
2022-12-04 09:02:24.779 I/test: log count:5
2022-12-04 09:02:25.779 I/test: log count:5

从log来看,动画会导致composable走很多次。但是当count为1的时候打印的次数最多,而后面的相对打印次数比较少,次数也不一定相同,所以说虽然执行的条件是一样的,但是重组的次数也是有差别的,所以不要妄图使用composable的重组来达到某种附带效应,这种做法的结果是不可预期的,还是老老实实的使用remember比较好一点。 从上面的例子可以看出,重组函数重组的调用点就是一个节点,state影响的也是当前的节点,所以尽量不要把所有的组件都写在一个composable函数里面,使用一级一级组装的形式来进行是比较好的一种方式,在官网里面也是这个建议使用的。