如何在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的修正方案
如果暂时不希望引入协程改造,可通过修改现有代码解决问题:
- 改造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 }
- 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
相关产品推荐
相关产品推荐

