如何使用 Kotlin 延迟加载协程刷新 ViewModel 数据?

cpg*_*en2 3 android coroutine kotlin kotlin-coroutines

我正在尝试在我正在构建的天气应用程序中实现 SwipeToRefreshLayout。当用户滑动刷新时,应该更新ViewModel中的数据,然后相应地更新视图。

这是我的 CurrentWeatherFragment 的片段:

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory)
                .get(WeatherResponseViewModel::class.java)

        pullToRefresh.setOnRefreshListener(this)

        bindUI()
    }

    override fun onRefresh() {
        viewModel = ViewModelProviders.of(this, viewModelFactory)
                .get(WeatherResponseViewModel::class.java)

        bindUI()
        pullToRefresh.isRefreshing = false
    }

    private fun bindUI() = launch {
        val currentWeather = viewModel.weather.await()
        currentWeather.observe(this@CurrentWeatherFragment, Observer {
            if (it == null) return@Observer

            loading_icon.visibility = View.GONE

            updateLocation("Raleigh")
            updateDateToToday()
            updateTemperatures(it.currently.temperature.roundToInt(),
                    it.currently.apparentTemperature.roundToInt(),
                    it.daily.data[0].temperatureMin.roundToInt(),
                    it.daily.data[0].temperatureMax.roundToInt())
            updateDescription(it.currently.summary)
            updateEnvironmentals((it.currently.humidity * 100).roundToInt(), it.currently.windSpeed.roundToInt())
            updateWeatherIcon(it.currently.icon)
            updateTempChart(it)
            updatePrecipChart(it)
        })
    }
Run Code Online (Sandbox Code Playgroud)

我的视图模型:

class WeatherResponseViewModel (
        private val forecastRepository: ForecastRepository,
        unitProvider: UnitProvider
) : ViewModel() {

    private val unitSystem = unitProvider.getUnitSystem()

    val isMetric: Boolean
        get() = unitSystem == UnitSystem.METRIC

    val weather by lazyDeferred {
        forecastRepository.getWeather()
    }

}
Run Code Online (Sandbox Code Playgroud)

和我的 lazyDeferred 实现:

fun <T> lazyDeferred(block: suspend CoroutineScope.() -> T): Lazy<Deferred<T>> {
    return lazy {
        GlobalScope.async {
            block.invoke(this)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

目前,使用此设置,在应用启动或切换到片段时加载片段时,将调用 ViewModel 中的 forecastRepository.getWeather() 函数,但在滑动刷新时,不会调用它。如何让 ViewModel 中的天气变量更新,以便视图可以观察到变化?

MJ *_*dio 7

首先,你的代码中有一些关于 Coroutine 和 ViewModel 的错误实现。

  1. 不要在onRefresh.

    相反,当 SwipeRefreshLayout 被拉出时,只需在你的视图模型中创建和调用 refresh 方法。

  2. 如果可以,请不要在您的应用程序中使用 GlobalScope。

    它可能导致工作泄漏并且不遵循结构化并发规则。

    相反,使用coroutineScope{}块。

  3. 不要使用LifecycleOwnerFragment观察LiveData的片段。它可以为 生成重复的观察者LiveData

    相反,使用viewLifecycleOwner片段的实例。

    这是因为当片段在返回堆栈中恢复时,片段的视图被重新创建,但片段。

    如果您将观察者生命周期与片段中的视图相匹配,Ovserver则视图销毁后 s 将不会保留。


刷新方式

不要使用惰性块。像@Stanislav Bondar's answer一样似乎没用。