You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现首个Flow发射后触发第二个Flow并随其变更实时更新?

问题描述

已实现API调用功能,希望允许用户选择排序顺序并将该选择保存至Datastore Preferences中。但由于排序选择与API数据均以Flow形式返回,无法实现让第二个Flow等待首个Flow发射数据,且在首个Flow发射新值时自动更新第二个Flow的逻辑。

示例代码

PreferencesRepository 相关

interface PreferencesRepository {
    fun getFilter(): Flow<OrderType>
}

class PreferencesRepositoryImpl @Inject constructor() : PreferencesRepository {
    override fun getFilter(): Flow<OrderType> = flow {
        delay(timeMillis = 300) // 模拟从datastore读取的初始延迟
        emit(value = OrderType.ASCENDING)
        delay(timeMillis = 3000) // 模拟3秒后排序方式变更
        emit(value = OrderType.DESCENDING)
    }
}

ItemsRepository 相关

interface ItemsRepository {
    fun getItems(order: OrderType): Flow<List<ItemModel>>
}

class ItemsRepositoryImpl @Inject constructor() : ItemsRepository {
    private val dummyItems: List<ItemModel> = listOf(
        ItemModel(id = 1, title = "first item"),
        ItemModel(id = 2, title = "second item"),
        ItemModel(id = 3, title = "third item")
    )

    override fun getItems(order: OrderType): Flow<List<ItemModel>> = flow {
        delay(timeMillis = 1000) // 模拟网络请求延迟
        when (order) {
            OrderType.ASCENDING -> {
                emit(value = dummyItems)
            }
            OrderType.DESCENDING -> {
                emit(value = dummyItems.sortedByDescending { it.id })
            }
        }
    }
}

HomeViewModel 代码

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val itemsRepository: ItemsRepository,
    private val preferencesRepository: PreferencesRepository
) : ViewModel() {
    private val _filter = MutableLiveData<OrderType>()
    val filter: LiveData<OrderType> get() = _filter

    private val _items = MutableLiveData<List<ItemModel>>()
    val items: LiveData<List<ItemModel>> get() = _items

    init {
        // 我觉得这两个方法必须整合才能正常工作
        getFilter()
        getItems()
    }

    private fun getFilter() = viewModelScope.launch {
        preferencesRepository.getFilter().collectLatest { orderType ->
            _filter.value = orderType
            println(orderType.name)
        }
    }

    private fun getItems() = viewModelScope.launch {
        itemsRepository.getItems(
            order = _filter.value ?: OrderType.ASCENDING // 无法如预期更新
        ).collectLatest { items ->
            _items.value = items
            items.forEach { println(it.title) }
        }
    }
}
当前Logcat输出

Logcat输出

解决方案

核心问题在于当前getItems方法仅在初始化时获取一次过滤器值,后续过滤器更新不会触发新的API请求。可以通过Flow的flatMapLatest操作符关联两个Flow,实现过滤器更新时自动重新请求数据:

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val itemsRepository: ItemsRepository,
    private val preferencesRepository: PreferencesRepository
) : ViewModel() {
    // 将过滤器Flow转为StateFlow,替代LiveData更贴合Flow使用场景
    private val filterFlow = preferencesRepository.getFilter()
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = OrderType.ASCENDING
        )

    // 基于过滤器Flow自动触发数据请求
    val items = filterFlow
        .flatMapLatest { orderType ->
            itemsRepository.getItems(orderType)
        }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )

    // 若仍需LiveData供View观察,可转换为LiveData
    val filter: LiveData<OrderType> = filterFlow.asLiveData()
}

代码说明

  1. stateIn:将冷Flow转为热StateFlow,支持多观察者共享数据,同时配置WhileSubscribed在View停止订阅时自动停止上游Flow,避免资源浪费。
  2. flatMapLatest:每当过滤器Flow发射新值时,会取消未完成的旧API请求,并立即发起新请求,确保数据始终匹配最新的排序规则。
  3. 移除冗余的MutableLiveData,通过Flow链式调用实现逻辑,代码更简洁且符合Jetpack架构最佳实践。

内容的提问来源于stack exchange,提问作者Denis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:36:18