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 } }
代码说明
flatMapLatest:将顶级分类流转换为最终的Catalogue流,确保仅最新的顶级分类请求会触发子分类请求(若有重复请求,会取消之前的子请求)- 子分类流转换:每个顶级分类对应独立的子分类请求流,通过
copy方法创建新的分类对象并设置子分类,避免修改原对象 combine并行合并:传入所有子分类流,当所有流都返回结果后,自动合并为更新后的顶级分类列表,再组装成Catalogue- 调度器优化:网络请求属于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
相关产品推荐
相关产品推荐

