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

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()
        }
    }
}

关键优化点说明

  1. 移除嵌套订阅:避免重复创建Flow收集器,减少资源浪费和UI重复更新
  2. ViewModel承担数据处理:将实体转Model的逻辑从Fragment移至ViewModel,符合MVVM架构职责
  3. 控制Flow发射次数:使用take(1)仅获取一次就绪后的数据,无需持续监听数据库变化
  4. 合并状态与数据流:直接在ViewModel中结合API请求状态和数据Flow,确保仅在数据就绪时发射有效结果
  5. 去重状态发射:用distinctUntilChanged过滤重复的加载状态,减少不必要的UI刷新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:51:01