android Jetpack编写智能重新组合

ajsxfq5m  于 2022-11-27  发布在  Android
关注(0)|答案(1)|浏览(195)

我正在做实验来理解重组和智能重组,并制作了一个样本

很抱歉,这些颜色是用Random.nextIn()生成的,以便直观地观察重组,设置颜色对重组没有影响,也可以在不改变颜色的情况下尝试。
gif中的内容由三部分组成

样品1

@Composable
private fun Sample1() {

    Column(
        modifier = Modifier
            .background(getRandomColor())
            .fillMaxWidth()
            .padding(4.dp)
    ) {
        var counter by remember { mutableStateOf(0) }

        Text("Sample1", color = getRandomColor())

        Button(
            modifier = Modifier
                .fillMaxWidth()
                .padding(vertical = 4.dp),
            colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
            onClick = {
                counter++
            }) {
            Text("Counter: $counter", color = getRandomColor())
        }
    }
}

我在这里没有问题,因为智能合成按预期工作,顶部的Text不阅读counter中的更改,所以重组只发生在Button内部的Text

样品2

@Composable
private fun Sample2() {
    Column(
        modifier = Modifier.background(getRandomColor())
    ) {

        var update1 by remember { mutableStateOf(0) }
        var update2 by remember { mutableStateOf(0) }

        println("ROOT")
        Text("Sample2", color = getRandomColor())

        Button(
            modifier = Modifier
                .padding(start = 8.dp, end = 8.dp, top = 4.dp)
                .fillMaxWidth(),
            colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
            onClick = {
                update1++
            },
            shape = RoundedCornerShape(5.dp)
        ) {

            println("🔥 Button1️")

            Text(
                text = "Update1: $update1",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )

        }

        Button(
            modifier = Modifier
                .padding(start = 8.dp, end = 8.dp, top = 2.dp)
                .fillMaxWidth(),
            colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
            onClick = { update2++ },
            shape = RoundedCornerShape(5.dp)
        ) {
            println("🍏 Button 2️")

            Text(
                text = "Update2: $update2",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )
        }

        Column(
            modifier = Modifier.background(getRandomColor())
        ) {

            println("🚀 Inner Column")
            var update3 by remember { mutableStateOf(0) }

            Button(
                modifier = Modifier
                    .padding(start = 8.dp, end = 8.dp, top = 2.dp)
                    .fillMaxWidth(),
                colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
                onClick = { update3++ },
                shape = RoundedCornerShape(5.dp)
            ) {

                println("✅ Button 3️")
                Text(
                    text = "Update2: $update2, Update3: $update3",
                    textAlign = TextAlign.Center,
                    color = getRandomColor()
                )

            }
        }

        Column() {
            println("☕️ Bottom Column")
            Text(
                text = "Sample2",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )
        }

    }
}

它还按预期工作,每个mutableState仅更新它们已被观察到的作用域。当更新这些mutableState中的任何一个时,仅观察update2update3Text被更改。

样品3

@Composable
private fun Sample3() {
    Column(
        modifier = Modifier.background(getRandomColor())
    ) {

        var update1 by remember { mutableStateOf(0) }
        var update2 by remember { mutableStateOf(0) }

        println("ROOT")
        Text("Sample3", color = getRandomColor())

        Button(
            modifier = Modifier
                .padding(start = 8.dp, end = 8.dp, top = 4.dp)
                .fillMaxWidth(),
            colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
            onClick = {
                update1++
            },
            shape = RoundedCornerShape(5.dp)
        ) {

            println("🔥 Button1️")

            Text(
                text = "Update1: $update1",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )

        }

        Button(
            modifier = Modifier
                .padding(start = 8.dp, end = 8.dp, top = 2.dp)
                .fillMaxWidth(),
            colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
            onClick = { update2++ },
            shape = RoundedCornerShape(5.dp)
        ) {
            println("🍏 Button 2️")

            Text(
                text = "Update2: $update2",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )
        }

        Column {

            println("🚀 Inner Column")
            var update3 by remember { mutableStateOf(0) }

            Button(
                modifier = Modifier
                    .padding(start = 8.dp, end = 8.dp, top = 2.dp)
                    .fillMaxWidth(),
                colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
                onClick = { update3++ },
                shape = RoundedCornerShape(5.dp)
            ) {

                println("✅ Button 3️")
                Text(
                    text = "Update2: $update2, Update3: $update3",
                    textAlign = TextAlign.Center,
                    color = getRandomColor()
                )

            }
        }
       // 🔥🔥 Reading update1 causes entire composable to recompose
        Column(
            modifier = Modifier.background(getRandomColor())
        ) {
            println("☕️ Bottom Column")
            Text(
                text = "Update1: $update1",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )
        }
    }
}

Sample2Sample3之间的唯一区别是底部的Text是阅读update1 mutableState,这会导致整个可组合对象被重新组合。正如您在gif中看到的,更改update1会重新组合或更改Sample3的整个颜色架构。

重新组合整个可组合的原因是什么?

Column(
            modifier = Modifier.background(getRandomColor())
        ) {
            println("☕️ Bottom Column")
            Text(
                text = "Update1: $update1",
                textAlign = TextAlign.Center,
                color = getRandomColor()
            )
        }
    }
ie3xauqp

ie3xauqp1#

要想让智能重组示波器发挥关键作用,您可以查看Vinay Gaba的What is “donut-hole skipping” in Jetpack Compose?文章。
Leland Richardson在此tweet中解释为
“跳过甜甜圈”的部分是这样一个事实:传入可组合(即Button)的新lambda可以重新组合,而无需重新编译它的其余部分。lambda是重新组合作用域这一事实对于您能够做到这一点是必要的,但还不够
换句话说,可组合的lambda是“特殊的”:)
我们很久以前就想这样做,但觉得太复杂了,直到@chuckjaz聪明地认识到,如果lambda是状态对象,调用是读,那么结果就是这样
您还可以在这里查看有关智能重组的其他答案,以及here
https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
当一个State被读取时,它会触发最近作用域中的重组。作用域是一个没有用inline标记并返回Unit的函数。Column、Row和Box是内联函数,因此它们不会创建作用域。
创建了RandomColorColumn,它接受其他Composables及其作用域content: @Composable () -> Unit

@Composable
fun RandomColorColumn(content: @Composable () -> Unit) {

    Column(
        modifier = Modifier
            .padding(4.dp)
            .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
            .background(getRandomColor())
            .padding(4.dp)
    ) {
        content()
    }
}

并更换

Column(
        modifier = Modifier.background(getRandomColor())
    ) {
        println("☕️ Bottom Column")
        Text(
            text = "Update1: $update1",
            textAlign = TextAlign.Center,
            color = getRandomColor()
        )
    }
}

RandomColorColumn() {

        println("☕️ Bottom Column")
        /*
            🔥🔥 Observing update(mutableState) does NOT causes entire composable to recompose
         */
        Text(
            text = "🔥 Update1: $update1",
            textAlign = TextAlign.Center,
            color = getRandomColor()
        )
    }
}

只有此范围会按预期更新,并且我们可以进行智能重组。

导致TextColumn内的任何可组合的没有作用域,从而在mutableState值改变时被重新组合的是Column,其在函数签名中具有inline关键字。

@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
    )
}

如果将inline添加到RandomColorColumn函数签名,您将看到它会导致整个Composable重新组合
Compose使用定义为
调用位置是调用可组合对象的源代码位置。这会影响它在组合中的位置,从而影响UI树。
如果在重新组合期间,可组合对象调用的可组合对象与它在前一个组合期间调用的可组合对象不同,则Compose将标识哪些可组合对象被调用或未被调用,对于在两个组合中都被调用的可组合对象,如果它们的输入未更改,Compose将避免重新组合它们。
请考虑以下示例:

@Composable
fun LoginScreen(showError: Boolean) {
    if (showError) {
        LoginError()
    }
    LoginInput() // This call site affects where LoginInput is placed in Composition
}

@Composable
fun LoginInput() { /* ... */ }

可组合函数的调用位置会影响智能重组,在可组合函数中使用inline关键字会将其子可组合函数的调用位置设置为同一级别,而不是低一级。
对于任何感兴趣的人这里是github repo玩/测试重组

相关问题