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

时间:2019-08-11 02:49:19

标签: android kotlin coroutine 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)
        })
    }

我的ViewModel:

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

}

和我的lazyDeferred实现:

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

当前,使用此设置,在应用程序启动时或在切换至片段时加载片段时,会调用ViewModel中的ForecastRepository.getWeather()函数,但在滑动刷新时不会调用该片段。如何在ViewModel中获取天气变量进行更新,以便视图可以观察到变化?

2 个答案:

答案 0 :(得分:3)

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

  1. 请勿在{{1​​}}中再次实例化ViewModel。

    相反,只要在以下情况下在视图模型中创建并调用refresh方法 SwipeRefreshLayout被拉。

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

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

    请使用onRefresh块。

  3. 请勿使用coroutineScope{}中的LifecycleOwner来观察片段Fragment。它可以为LiveData生成重复的观察者。

    相反,请使用LiveData个片段实例。

    这是因为,当将片段恢复到堆栈中时,会重新创建片段的视图,但会重新创建片段。

    如果您将观察者生命周期与片段视图匹配,则viewLifecycleOwner不会在视图被破坏后保留。


刷新方式

不要使用惰性块。好像Ovserver的答案一样没用。

答案 1 :(得分:1)

来自Kotlin委派的属性:

  

惰性属性:仅在首次访问时才计算该值;

请勿将lazy用于可变数据