简体   繁体   English

Android Compose 中的哑重组

[英]Dumb recomposition in Android Compose

Consider this minimal code snippet (in Kotlin):考虑这个最小的代码片段(在 Kotlin 中):

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import java.time.LocalDateTime
import java.util.*

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {

            var time by remember {
                mutableStateOf("time")
            }
            Column(modifier = Modifier.clickable { time = LocalDateTime.now().toString() }) {
                Text(text = UUID.randomUUID().toString())
                Text(text = time)
            }
        }
    }
}

Examining the above code, from a logical perspective, one expects that upon clicking the Column , since only the parameter time changes, only the lower time Text composable would be redrawn.检查上面的代码,从逻辑的角度来看,人们期望在单击Column时,由于只有参数time发生变化,因此只有较低 time 的Text composable 会被重绘。 This is because recomposition skips as much as possible .这是因为重组尽可能地跳过

However, one finds that the upper Text composable is being redrawn too (the displayed UUID keeps changing).然而,人们发现上面的Text composable 也在重绘(显示的 UUID 不断变化)。

  1. Why is this?为什么是这样?

Notice that the non-idempotence of my Column composable should have no bearing unless the redraw is dumb.请注意,除非重绘是愚蠢的,否则我的Column可组合的非幂等性应该没有影响。

You can try running this block of code您可以尝试运行此代码块

@Composable
fun IdempotenceTest() {    
    var time by remember {
        mutableStateOf("time")
    }
    Column(
        modifier = Modifier.clickable {
            time = LocalDateTime.now().toString()
        }
    ) {
        Text(text = getRandomUuid())
        TestComposable(text = returnSameValue())
        Text(text = time)
    }
}

@Composable
fun TestComposable(text: String) {
    SideEffect {
        Log.d(TAG, "TestComposable composed with: $text")
    }
    Text(text = text)
}

private fun getRandomUuid(): String {
    Log.d(TAG, "getRandomUuid: called")
    return UUID.randomUUID().toString()
}

private fun returnSameValue(): String {
    Log.d(TAG, "returnSameValue: called")
    return "test"
}

If you check the logs, you will see that everytime the Column recomposes, the compiler will call the getRandomUuid and returnSameValue function and based on the value returned by these, it will decide whether or not to re-compose.如果查看日志,您会看到每次Column重组时,编译器都会调用getRandomUuidreturnSameValue function 并根据这些返回的值决定是否重组。 If you want to prevent a calculation from happening again and again, wrap it in a remember{} block.如果您想防止一次又一次地发生计算,请将其包装在一个remember{}块中。

As a side-note, the Composable functions must be side-effect free to ensure idempotence.作为旁注,可组合函数必须是无副作用的,以确保幂等性。 You can read more here你可以在这里阅读更多

rememberSavable {...} worked for me. rememberSavable {...}为我工作。

It allows you to update the state, directly from clickable and trigger recomposition:它允许您直接从clickable和触发重组更新 state:

...
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
...

var time by rememberSaveable { mutableStateOf("time") }
Column(
    modifier = Modifier.clickable { 
        time = LocalDateTime.now().toString() 
    }
) {
    Text(text = UUID.randomUUID().toString())
    Text(text = time)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM