android Jetpack编写范围/智能重组

yzuktlbb  于 2023-09-28  发布在  Android
关注(0)|答案(1)|浏览(101)

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

对不起,颜色,它们是用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,这导致整个composable被重新组合。正如您在gif中看到的,更改update1会重新组合或更改Sample3的整个颜色模式。

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

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

emeijp431#

智能重组作用域扮演着关键的角色。您可以查看Vinay Gaba的What is “donut-hole skipping” in Jetpack Compose?文章。
Leland Richardson在这篇tweet中解释为
“甜甜圈跳过”的部分是这样一个事实,即一个新的lambda被传递到一个可组合的(即按钮)可以重新组合,而无需重新编译它的其余部分。事实上,lambda是重组作用域,这对您能够做到这一点是必要的,但还不够
换句话说,可组合的lambda是“特殊的”:)
我们想这么做很久了,但觉得太复杂了,直到@chuckjaz有了一个绝妙的认识,如果lambda是状态对象,调用是读,那么这就是结果
你也可以在这里查看关于智能重组和here的其他答案。
https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
当读取状态时,它触发最近范围中的重组。而作用域是一个没有标记为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中的任何Composable没有作用域,从而在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
    )
}

如果你在RandomColorColumn函数签名中添加inline,你会看到它会导致整个Composable重新组合
合成使用定义为
调用站点是调用组合对象的源代码位置。这影响了它在Composition中的位置,因此也影响了UI树。
如果在重新组合期间,可组合对象调用的可组合对象与在先前组合期间调用的可组合对象不同,则“组合”将识别哪些可组合对象被调用或未被调用,并且对于在两个组合中都被调用的可组合对象,如果它们的输入未更改,则“组合”将避免重新组合它们。
请考虑以下示例:

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

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

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

相关问题