android 为什么在将StateFlow作为属性的ViewModel上调用逻辑方法会导致重新组合,即使值没有更改?

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

当按下C键时,这个应用程序计算A。由于A是唯一改变的,我期望重组就是这样。但是C也是重组。

下面是代码。
ViewModel公开状态流。

class MainViewModel : ViewModel() {
  private val _count: MutableStateFlow<Int> = MutableStateFlow(0)
  val count: StateFlow<Int> = _count.asStateFlow()
  fun increaseCount() {
    _count.value++
  }
}

CCompose调用increaseCount()

@Composable
fun CountUpScreen(
  modifier: Modifier = Modifier,
  viewModel: MainViewModel = viewModel(),
) {
  val count: Int by viewModel.count.collectAsState()
  SideEffect { println("CountUpScreen") }
  Column(
    modifier = modifier.fillMaxSize(),
    verticalArrangement = Arrangement.SpaceEvenly,
    horizontalAlignment = Alignment.CenterHorizontally,
  ) {

    ACompose(
      count = count
    )
    BCompose()
    CCompose {
      viewModel.increaseCount()
    }
  }
}

@Composable
private fun ACompose(count: Int) {
  SideEffect { println("ACompose") }
  Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
    Text(
      text = "$count"
    )
  }
}

@Composable
private fun BCompose() {
  SideEffect { println("BCompose") }
  Text(
    text = "I am composable that will not be recompose"
  )
}

@Composable
private fun CCompose(onClick: () -> Unit) {
  SideEffect { println("CCompose") }
  Button(onClick = {
    onClick()
  }) {
    Icon(Icons.Outlined.Add, contentDescription = "+")
  }
}

以下是进行累加的日志的结果。

I/System.out: CountUpScreen
I/System.out: ACompose
I/System.out: CCompose

CCompose为何要重新撰写?

deyfvvtc

deyfvvtc1#

这里发生了一些事情。
首先,Compose编译器不会自动记住捕获不稳定类型的lambda。ViewModel是一个不稳定的类,因此在onClick lambda中使用它来捕获它意味着每次重组时都将重新创建onClick
因为lambda正在被重新创建,所以CCompose的输入在各个重构中不相等,因此CCompose被重构。
这是Compose编译器的当前行为,但将来很可能会改变,因为我们知道这是一种常见的情况,我们可以做得更好。
如果你想解决这个问题,你可以自己记住lambda。

val cComposeOnClick = remember(viewModel) { { viewModel.increaseCount() } }
CCompose(onClick = cComposeOnClick)

或者将ViewModel更改为具有lambda而不是increaseCount函数。

class MyViewModel {
  val increaseCount = { ... }
}

或者您可以在技术上使用@Stable对ViewModel类进行注解,但我可能不推荐这样做,因为正确维护稳定契约将非常困难。
但我也想提一下,如果CCompose只是一个小的可组合组件,那么额外的一次重新组合可能不会对应用的性能产生太大影响,只有当它确实给你带来了问题时,你才会应用这些修复。
稳定性是一个相当大的主题,我建议阅读this post了解更多信息。

相关问题