我有一个典型的 Android MVVM 项目(Compose -> ViewModel -> Repository -> RoomDB),并在下面的演示中简化了概念。
我想实现什么目标?:
我想从我的存储库中的 Room Dao (@Query(...) fun getAll(): Flow) 读取两个流。它们应该组合成一个流,然后返回到我的 viewModel。
我想将 viewModel 中的流转换为 StateFlow,只要 viewModel 存在,它就可以进行观察。在下面的示例中,我将此 StateFlow 值传播到可变状态“项目”,以便能够在 UI 中显示值以进行演示。
问题: 当我尝试调试时,流上的组合方法不会触发层次结构中备份的任何更改,单个 Dao 流上的collectLatest也不会触发。有趣的是,collect 确实...
现在开始演示:
class ViewModel(private val coroutineScope: CoroutineScope) {
private val repository = Repository()
val item = mutableStateOf<String?>(null)
init {
coroutineScope.launch {
repository.item.stateIn(coroutineScope).collect {
item.value = it
}
}
}
fun click() {
coroutineScope.launch {
repository.increaseItem()
}
}
}
class Repository {
private val dao = Dao()
val item: Flow<String> = flow {
// This does NOT trigger
dao.dbItem.combine(dao.dbItem2) { value1, value2 ->
emit("$value1|$value2")
}
// This does NOT trigger
// dao.dbItem.collectLatest {
// emit(it)
// }
// This does trigger...
// dao.dbItem.collect {
// emit(it)
// }
}
suspend fun increaseItem() = dao.increaseDbItem()
}
class Dao {
val dbItem = MutableStateFlow("0")
val dbItem2 = MutableStateFlow("A")
suspend fun increaseDbItem() {
dbItem.emit("${dbItem.value} 0")
dbItem2.emit("${dbItem2.value} A")
}
}
@Composable
fun AtteTest() {
val coroutineScope = rememberCoroutineScope()
val viewModel = ViewModel(coroutineScope)
TestAppTheme {
Surface {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Value:")
Text(viewModel.item.value ?: "")
}
Button(onClick = {
viewModel.click()
}) {
Text(text = "click")
}
}
}
}
}
}
@Preview
@Composable
fun PreviewAtteTest() {
AtteTest()
}
进行以下更改后即可工作:
返回联合收割机而不是包含联合收割机的新流
class Repository {
private val dao = Dao()
val item: Flow<String> = dao.dbItem2.combine(dao.dbItem) { value1, value2 ->
"$value1|$value2"
}
suspend fun increaseItem() = dao.increaseDbItem()
}
通过将流更改为回调流解决了collectLatest的问题:
val item: Flow<String> = callbackFlow {
dao.dbItem.collectLatest {
send(it)
}
awaitClose { }
}