首页 > 解决方案 > 多次从 ViewModel 调用一种方法的好解决方案

问题描述

我需要从 MVVM 多次调用一种方法以进行 API 调用并将结果发送到回收站视图。我不知道用户需要这样做多少次,因为它取决于列表大小。例如:

list: ["FirstElement", "SecondElement", "..", "..."]

在代码中,我尝试了循环中的调用方法:

 for (city in list) {
     viewModel.getCurrentWeatherByCity(city, appID, unit).observe(viewLifecycleOwner, {
     result.add(it) // here is arraylist for recyclerview
   })
 }
 val adapterRecycler = LocationListWeatherRecyclerAdapter(result) // init adapter for recycler
 setUpRecyclerData(adapterRecycler) // method for setup recyclerview

查看模型方法:

fun getCurrentWeatherByCity(city: String, appID: String, units: String): LiveData<WeatherModel> {
 return repository.getCurrentWeatherByCity(city, appID, units)
}

和存储库方法:


    fun getCurrentWeatherByCity(city: String, appID: String, units: String ) : LiveData<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())
                } else {
                }
            }

            override fun onFailure(call: Call<WeatherModel>, t: Throwable) {
            }
        })

        return weatherData
    }

但我知道这是错误的解决方案,因为结果是在后台做的并且是错误的。这 for 可以从循环中的 4 次迭代中返回数组中的 80 个项目。

你知道我能做些什么吗?感谢帮助

标签: androidkotlinmvvm

解决方案


使用挂起函数可以更轻松地解决这个问题,我就是这样做的。

首先让你的仓库使用一个直接返回的挂起函数WeatherModel而不是一个LiveData<WeatherModel>. 它可以在失败时返回 null,或者如果你愿意,你可以做一些更复杂的事情。

suspend fun getCurrentWeatherByCity(city: String, appID: String, units: String ) : WeatherModel? {
    return runCatching { apiService.getCurrentWeatherByCity(city, appID, units).await() }
        .getOrNull()
}

然后,您可以在 ViewModel 中创建一个函数,该函数接受城市列表并并行运行 api 调用,如下所示:

fun getCurrentWeatherByCities(cities: List<String>, appID: String, units: String) = liveData<List<WeatherModel>> {
    val weatherModels = cities.map { city -> 
        async { repository.getCurrentWeatherByCity(city, appID, units) }
    }
        .awaitAll()
        .filterNotNull()
    emit(weatherModels)
}

然后在你的片段中,观察它并在观察者中设置适配器列表。

viewModel.getCurrentWeatherByCities(cities, appID, unit).observe(viewLifecycleOwner, {
    val adapterRecycler = LocationListWeatherRecyclerAdapter(it)
    setUpRecyclerData(adapterRecycler)
}

推荐阅读