修改列表时,Jetpack composecollectAsState() 未收集热流

srv*_*rvy 12 kotlin kotlin-coroutines android-jetpack-compose kotlin-flow android-jetpack-compose-list

当我使用 时collectAsState()collect {}仅在传递新列表时触发,而不是在修改和发出列表时触发。

查看模型

@HiltViewModel
class MyViewModel @Inject constructor() : ViewModel() {
    val items = MutableSharedFlow<List<DataItem>>()
    private val _items = mutableListOf<DataItem>()

    suspend fun getItems() {
        _items.clear()

        viewModelScope.launch {
            repeat(5) {
                _items.add(DataItem(it.toString(), "Title $it"))
                items.emit(_items)
            }
        }

        viewModelScope.launch {
            delay(3000)
            val newItem = DataItem("999", "For testing!!!!")
            _items[2] = newItem
            items.emit(_items)
            Log.e("ViewModel", "Updated list")
        }
    }
}

data class DataItem(val id: String, val title: String)
Run Code Online (Sandbox Code Playgroud)

可组合的

@Composable
fun TestScreen(myViewModel: MyViewModel) {
    val myItems by myViewModel.items.collectAsState(listOf())

    LaunchedEffect(key1 = true) {
        myViewModel.getItems()
    }

    LazyColumn(
        modifier = Modifier.padding(vertical = 20.dp, horizontal = 10.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        items(myItems) { myItem ->
            Log.e("TestScreen", "Got $myItem") // <-- won't show updated list with "999"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望collect {}收到更新的列表,但事实并非如此。SharedFlow或者StateFlow没关系,两者的行为都是一样的。我可以让它工作的唯一方法是创建一个新列表并发出它。当我使用SharedFlow它时,无论是equals()returntrue还是false.

    viewModelScope.launch {
        delay(3000)
        val newList = _items.toMutableList()
        newList[2] = DataItem("999", "For testing!!!!")
        items.emit(newList)
        Log.e("ViewModel", "Updated list")
    }
Run Code Online (Sandbox Code Playgroud)

我不应该创建一个新列表。知道我做错了什么吗?

Phi*_*hov 22

您每次都会发出相同的对象。Flow不关心相等性并发出它 - 您可以尝试手动收集它来检查它,但 Compose 会尝试尽可能地减少重组次数,因此它会检查状态值是否实际上已更改。

由于您发出可变列表,因此相同的对象存储在可变状态值中。它无法跟踪该对象的更改,当您再次发出它时,它会进行比较并发现数组对象是相同的,因此不需要重新组合。您可以在这一行添加一个断点来查看发生了什么。

解决方案是将可变列表转换为不可变列表:每次发出时它都会成为一个新对象。

items.emit(_items.toImmutableList())
Run Code Online (Sandbox Code Playgroud)

另一种需要考虑的选择是使用mutableStateListOf

private val _items = mutableStateListOf<DataItem>()
val items: List<DataItem> = _items

suspend fun getItems() {
    _items.clear()

    viewModelScope.launch {
        repeat(5) {
            _items.add(DataItem(it.toString(), "Title $it"))
        }
    }

    viewModelScope.launch {
        delay(3000)
        val newItem = DataItem("999", "For testing!!!!")
        _items[2] = newItem
        Log.e("ViewModel", "Updated list")
    }
}
Run Code Online (Sandbox Code Playgroud)