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

Android Kotlin Flow:如何等待所有Flow完成并正确获取子分类?

Kotlin Flow 并行请求子分类问题解决

我正在学习Android平台的Kotlin Flow操作符,想要先执行网络或数据库请求获取顶级分类,再为每个顶级分类发起并行的子分类请求,等待所有子分类请求返回后组装完整的Catalogue对象。相关数据类和请求方法定义如下:

data class Category(var id: Int, var name: String, var parentCategoryId: Int?) {
    var subCategories: List<Category> = listOf()
}

data class Catalogue(var categories: List<Category>) {}

// request to fetch top level categories
fun getTopCats(): Flow<List<Category>> {
    return flowOf(
        listOf(
            Category(0, "Dairy", null),
            Category(1, "Fruits", null),
            Category(2, "Vegetables", null)
        )
    )
}

// request to fetch sub categories
suspend fun getSubCats(catId: Int): Flow<List<Category>> {
    return when (catId) {
        0 -> flowOf(listOf(Category(3, "Milk", 0), Category(4, "Butter", 0)))
            .onEach { delay(1000) }
        1 -> flowOf(
            listOf(
                Category(5, "Banana", 1),
                Category(6, "Mandarin", 1),
                Category(7, "Orange", 1)
            )
        ).onEach { delay(2000) }
        2 -> flowOf(
            listOf(
                Category(8, "Carrot", 2),
                Category(9, "Asparagus", 2),
                Category(10, "Lettuce", 2)
            )
        ).onEach { delay(3000) }
        else -> flowOf()
    }
}

我的首次尝试代码未成功获取子分类,请问combine操作符应放置在何处?尝试代码如下:

viewModelScope.launch {
    val catalogue = Catalogue(listOf())
    getTopCats().map { categories ->
        catalogue.categories = categories // assign top level category

        val flows = arrayListOf<Flow<List<Category>>>()
        categories.onEach { cat ->
            flows.add(getSubCats(cat.id))
        }
        combine(flows) { array ->
            array.onEach { list ->
                catalogue.categories[list.first().id].subCategories = list
            }
        }
        catalogue
    }.flowOn(Dispatchers.Default).collect() {
        Timber.d("Received catalogue object")
    }
}

问题分析

你的代码存在几个关键问题:

  • map操作符内创建了combine流,但未收集该流,导致子分类请求根本未执行
  • 直接修改Catalogue对象属性的方式不符合Flow响应式设计原则
  • 通过索引匹配分类ID的逻辑存在隐患(仅当前测试数据ID与索引一致,不具备通用性)

修正后的代码

viewModelScope.launch {
    getTopCats()
        .flatMapLatest { topCategories ->
            // 为每个顶级分类创建子分类请求流,并转换为带子类的分类对象
            val subCatFlows = topCategories.map { topCat ->
                getSubCats(topCat.id)
                    .map { subCats -> topCat.copy(subCategories = subCats) }
            }
            // 并行等待所有子分类流完成,合并为最终Catalogue
            combine(subCatFlows) { updatedTopCats ->
                Catalogue(updatedTopCats.toList())
            }
        }
        .flowOn(Dispatchers.IO) // 网络请求优先使用IO调度器
        .collect { catalogue ->
            Timber.d("Received catalogue object: $catalogue")
            // 处理最终组装完成的Catalogue
        }
}

代码说明

  1. flatMapLatest:将顶级分类流转换为最终的Catalogue流,确保仅最新的顶级分类请求会触发子分类请求(若有重复请求,会取消之前的子请求)
  2. 子分类流转换:每个顶级分类对应独立的子分类请求流,通过copy方法创建新的分类对象并设置子分类,避免修改原对象
  3. combine并行合并:传入所有子分类流,当所有流都返回结果后,自动合并为更新后的顶级分类列表,再组装成Catalogue
  4. 调度器优化:网络请求属于IO密集型任务,使用Dispatchers.IO比Dispatchers.Default更合适

另外,建议将数据类改为不可变设计(用val替代var),符合Kotlin最佳实践:

data class Category(
    val id: Int,
    val name: String,
    val parentCategoryId: Int?,
    val subCategories: List<Category> = emptyList()
)

data class Catalogue(val categories: List<Category>)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 12:30:42