kotlin 为什么ViewModel方法引用不稳定(导致重组)而lambda稳定?(Jetpack合成)

fdbelqdn  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(143)

我有一个MVI体系结构与活动包含主屏幕

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      MyTheme {
        val viewModel: MainScreenViewModel = viewModel(factory = MainScreenViewModel.Factory)
        val state by viewModel.viewState.collectAsState()
        MainScreen(state, viewModel::handleEvent)
      }
    }
  }
}

主屏幕看起来有点像这样:

@Composable
fun MainScreen(state: MainScreenState, onEvent: (Event) -> Unit) {
  TaskList(
    tasks = state.items,
    onEvent = onEvent,
  )
}

@Composable
fun TaskList(tasks: List<TaskState>, onEvent: (Event) -> Unit) {
  LazyColumn {
    items(items = tasks, key = { it.id }) {
      Task(state = it, onEvent = onEvent)
    }
  }
}

现在,如果ViewModel发出一个新的MainScreenState,其中一个TaskState被更改,则TaskList将重新组合。这是预期的,因为传递了一个新的列表对象。但是不仅改变了的Task被重组,而且列表中每个Task可重组。原因不是TaskState,而是稳定。
导致重组的似乎是onEvent回调。如果我在MainScreen中将onEvent = onEvent更改为onEvent = { onEvent(it) },问题就解决了。但我不明白为什么。**方法引用也应该是稳定的吗?**我甚至检查了hashCode,它总是一样的。

rdlzhqv9

rdlzhqv91#

即使TaskState稳定,Task组合也会重新组合的原因是因为onEvent回调是通过引用传递的。这意味着当TaskList组合对象被重新组合时,它将获得onEvent回调的新副本。这个新的回调副本不知道TaskState的先前状态,因此它使用新状态调用onEvent函数。
要解决这个问题,可以使用lambda表达式为每个Task组合创建一个新的onEvent回调。这样,每个Task组合都将拥有自己的onEvent回调副本,并且它将知道TaskState的先前状态。
下面是更新后的代码:

@Composable
fun MainScreen(state: MainScreenState) {
  TaskList(
    tasks = state.items,
    onEvent = { event -> viewModel.handleEvent(event) }
  )
}

@Composable
fun TaskList(tasks: List<TaskState>, onEvent: (Event) -> Unit) {
  LazyColumn {
    items(items = tasks, key = { it.id }) {
      Task(state = it, onEvent = onEvent)
    }
  }
}

相关问题