Android Compose 中的哑重组
Posted
技术标签:
【中文标题】Android Compose 中的哑重组【英文标题】:Dumb recomposition in Android Compose 【发布时间】:2022-01-13 22:58:35 【问题描述】:考虑这个最小的代码 sn-p(在 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)
检查上面的代码,从逻辑的角度来看,人们预计在单击Column
时,由于只有参数time
发生了变化,因此只有较低的时间Text
可组合被重绘。这是因为recomposition skips as much as possible。
然而,我们发现上面的 Text
组合也被重绘了(显示的 UUID 不断变化)。
-
这是为什么呢?
请注意,我的 Column
可组合的非幂等性应该没有任何影响,除非重绘是愚蠢的。
【问题讨论】:
【参考方案1】:你可以试试运行这段代码
@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"
如果您检查日志,您将看到每次状态更改时,编译器都会尝试重新调用正在读取状态值的最小封闭 lamda/函数。因此,IdempotenceTest 函数(在我的示例中和您的 setContent lamda 中)将被重新执行,它将调用 getRandomUuid
和 returnSameValue
函数,并根据这些返回的值决定是否重新组合依赖于这些返回值的元素。如果您想防止计算一次又一次地发生,请将其包装在 remember
块中。
现在,如果您使用 Button
代替 Column,您会看到只有相同的内容 lamda 会被执行。发生这种情况的原因是 Column 是一个内联函数,而 Button 本身在其中使用了非内联的 Surface。因此Column
的内容被复制到封闭的功能块内,从而导致整个IdempotenceTest
被重新组合无效。
附带说明,可组合函数必须是无副作用的,以确保幂等性。你可以阅读更多here。
要了解更多关于重组范围的信息,您可以参考博文here 和here。
【讨论】:
“每次 Column 重组”..这是问题的本质......为什么整个 Column 首先要重组? "编译器会调用 getRandomUuid..." 为什么?编译器不应该只调用观察time
的代码吗?
我已编辑答案以包含有关重组范围的更多信息。
扎克的帖子真的把事情弄清楚了..thnx【参考方案2】:
rememberSavable ...
为我工作。
它允许您直接从clickable
更新状态并触发重组:
...
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)
【讨论】:
以上是关于Android Compose 中的哑重组的主要内容,如果未能解决你的问题,请参考以下文章