Android中一次性获取Flow值效率低,如何优化代码?
优化方案及代码调整
核心问题分析
现有代码的主要低效点在于:Fragment中嵌套collect会导致每次isDynamicDataLoaded变为true时都创建新的Flow订阅,造成资源冗余;同时数据转换逻辑放在Fragment中违背MVVM职责划分;Room的Flow默认持续监听数据库变化,而你只需要一次性获取就绪后的数据。
优化后的ViewModel实现
将数据组合、状态判断、Model转换逻辑全部移至ViewModel,同时控制Flow仅在数据就绪后发射一次结果:
class StoreModeHomeViewModel : ViewModel() { // 组合后的UI数据Flow:仅在两个API完成后发射一次结果 val combinedOffers: Flow<Pair<List<PersonalOfferModel>, List<FavoriteOfferModel>>> = combine( PersonalOffersRepository.instance.getPersonalOffers(), ProductsRepository.instance.getFavoriteOfferProductsLiveData().asFlow(), getRequestCompletionState() ) { personalOffers, favoriteDBProducts, isAllRequestsCompleted -> if (isAllRequestsCompleted) { // 完成实体到UI Model的转换 val personalModels = personalOffers.map { PersonalOfferModel(it) } val favoriteModels = favoriteDBProducts.map { FavoriteOfferModel(Product.mapFromDB(it)) } personalModels to favoriteModels } else { null } }.filterNotNull() // 过滤未就绪的无效数据 .take(1) // 仅发射一次结果,避免持续监听数据库 // 对外暴露加载状态Flow val isLoading: Flow<Boolean> = getRequestCompletionState().map { !it } .distinctUntilChanged() // 避免重复发射相同状态 private fun getRequestCompletionState(): Flow<Boolean> { val personalRequestDone = PersonalOffersRepository.instance.getOffersApiRequestStatus() .map { it?.status == ApiRequestStatus.COMPLETED } .distinctUntilChanged() val favoriteRequestDone = ProductsRepository.instance.getFavoriteOffersApiRequestStatusLiveData() .map { it?.status == ApiRequestStatus.COMPLETED } .distinctUntilChanged() return combine(personalRequestDone, favoriteRequestDone) { pDone, fDone -> pDone && fDone } } fun loadData(restCaller: RestCaller) = CoreApplication.instance?.applicationContext?.let { val workers = arrayListOf( OneTimeWorkRequest.Builder(GetPersonalOffersWorker::class.java) .setInputData(restCaller.toData()) .setCancelable() .build(), OneTimeWorkRequest.Builder(GetFavoriteOfferProductsWorker::class.java) .setInputData(restCaller.toData()) .setCancelable() .build(), ) WorkManager.getInstance(it) .beginUniqueWork("store_mode_data_task", ExistingWorkPolicy.KEEP, workers) .enqueue() } }
优化后的Fragment实现
移除嵌套collect,直接订阅ViewModel提供的状态和数据Flow,逻辑更清晰:
lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { // 监听加载状态,控制进度条显示 viewModel.isLoading.collect { isLoading -> binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE } } } lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { // 订阅就绪后的组合数据,更新UI viewModel.combinedOffers.collect { (personalModels, favoriteModels) -> personalOffersModelList.apply { clear() addAll(personalModels) } updateTabCounter(SwimLaneItemType.PERSONAL_OFFERS, personalModels.size) favoriteOffersModelList.apply { clear() addAll(favoriteModels) } updateTabCounter(SwimLaneItemType.FAVOURITE_OFFERS, favoriteModels.size) updateTabCounter(SwimLaneItemType.ALL, personalModels.size + favoriteModels.size) selectTabAt(selectedTab) updateOrderPickUpItemView() } } }
关键优化点说明
- 移除嵌套订阅:避免重复创建Flow收集器,减少资源浪费和UI重复更新
- ViewModel承担数据处理:将实体转Model的逻辑从Fragment移至ViewModel,符合MVVM架构职责
- 控制Flow发射次数:使用
take(1)仅获取一次就绪后的数据,无需持续监听数据库变化 - 合并状态与数据流:直接在ViewModel中结合API请求状态和数据Flow,确保仅在数据就绪时发射有效结果
- 去重状态发射:用
distinctUntilChanged过滤重复的加载状态,减少不必要的UI刷新
内容的提问来源于stack exchange,提问作者Taimoor Khan
相关产品推荐
相关产品推荐

