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

如何在MVVM中多次调用ViewModel方法批量请求API并填充RecyclerView

问题根源
  • Repository中weatherData是全局单实例LiveData,每次调用接口都返回同一个实例,循环调用时多次注册观察者,任意请求返回都会触发所有观察者回调,导致数据被重复添加到结果列表
  • 接口请求为异步执行,初始化适配器时所有请求均未返回,此时列表为空,后续新增数据后也未通知适配器刷新
  • 异步请求返回顺序不固定,最终列表顺序无法和输入的城市列表保持一致
最优解决方案:Kotlin协程批量合并请求

Android官方推荐使用协程处理异步任务,可实现并发请求后统一返回结果,逻辑更简洁可控。

1. 改造Repository层

将原回调写法改为挂起函数,直接返回接口结果:

// Repository层代码
suspend fun getCurrentWeatherByCity(city: String, appID: String, units: String) : WeatherModel? {
    return try {
        val response = apiService.getCurrentWeatherByCity(city, appID, units).execute()
        if (response.isSuccessful) response.body() else null
    } catch (e: Exception) {
        null
    }
}

2. 改造ViewModel层

新增批量请求方法,用async并发发起所有请求,全部完成后合并结果抛出给UI层:

// ViewModel层代码
private val _weatherListLiveData = MutableLiveData<List<WeatherModel>>()
val weatherListLiveData: LiveData<List<WeatherModel>> = _weatherListLiveData

fun batchGetWeather(cityList: List<String>, appID: String, unit: String) = viewModelScope.launch {
    // 并发发起所有请求
    val deferredList = cityList.map { city ->
        async { getCurrentWeatherByCity(city, appID, unit) }
    }
    // 等待所有请求返回,过滤请求失败的空结果
    val resultList = deferredList.awaitAll().filterNotNull()
    _weatherListLiveData.postValue(resultList)
}

3. UI层调用

仅需观察一次LiveData,拿到结果后直接更新适配器即可:

// UI层(Activity/Fragment)代码
// 初始化适配器
val adapterRecycler = LocationListWeatherRecyclerAdapter(mutableListOf())
setUpRecyclerData(adapterRecycler)

// 观察批量请求结果
viewModel.weatherListLiveData.observe(viewLifecycleOwner) { resultList ->
    adapterRecycler.setNewData(resultList)
}

// 发起批量请求
viewModel.batchGetWeather(cityList, appID, unit)

适配器需要新增setNewData方法,内部更新数据集后调用notifyDataSetChanged,或使用DiffUtil实现局部刷新性能更优

兼容现有LiveData的修正方案

如果暂时不希望引入协程改造,可通过修改现有代码解决问题:

  1. 改造Repository每次返回新的LiveData实例,避免多个请求共用同一个实例导致的回调混乱:
// Repository修改后代码
fun getCurrentWeatherByCity(city: String, appID: String, units: String ) : LiveData<WeatherModel> {
    // 每次调用新建独立的LiveData实例
    val weatherData = MutableLiveData<WeatherModel>()
    apiService.getCurrentWeatherByCity(city, appID, units).enqueue(object : Callback<WeatherModel>{
        override fun onResponse(call: Call<WeatherModel>, response: Response<WeatherModel>) {
            if (response.isSuccessful) {
                weatherData.postValue(response.body())
            }
        }
        override fun onFailure(call: Call<WeatherModel>, t: Throwable) {}
    })
    return weatherData
}
  1. UI层统计请求完成数量,所有请求返回后再更新适配器:
val totalRequestCount = cityList.size
var finishedRequestCount = 0
val resultList = mutableListOf<WeatherModel>()

// 提前初始化适配器
val adapterRecycler = LocationListWeatherRecyclerAdapter(resultList)
setUpRecyclerData(adapterRecycler)

for (city in cityList) {
    viewModel.getCurrentWeatherByCity(city, appID, unit).observe(viewLifecycleOwner) { weather ->
        resultList.add(weather)
        finishedRequestCount++
        // 所有请求完成后统一更新列表
        if (finishedRequestCount == totalRequestCount) {
            adapterRecycler.setNewData(resultList)
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 12:06:00