LiveData和协程-属性必须初始化或抽象

时间:2019-04-16 17:49:59

标签: kotlin coroutine android-livedata kotlin-coroutines

我试图在MVVM中同时使用LiveData和协程,但我可能缺少一些简单的东西。

class WeatherViewModel (
    private val weatherRepository: ForecastRepository
) : ViewModel() {

    var weather: LiveData<Weather>;

    /**
     * Cancel all coroutines when the ViewModel is cleared.
     */
    @ExperimentalCoroutinesApi
    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()
    }


    init {
        viewModelScope.launch {
            weather = weatherRepository.getWeather()
        }

    }

}

但是我在Property must be initialized or be abstract函数中分配weather时得到了init。 我假设是这种情况,因为我正在使用协程viewModelScope.launch

override suspend fun getWeather(): LiveData<Weather> {
    return withContext(IO){
       initWeatherData()
       return@withContext weatherDao.getWeather()
    }
}

我该如何解决?

4 个答案:

答案 0 :(得分:0)

如下更改签名:

var weather =  MutableLiveData<Weather>();

此外,您应该只返回Weather对象,而不返回LiveData <> 因此您应该更改getWeather()的签名以返回: Weather

答案 1 :(得分:0)

您可以将weather属性声明为lateinit

private lateinit var weather: LiveData<String>

或将其设为 nullable

private var weather: LiveData<String>? = null

如果您确定在首次使用该属性之前会对其进行初始化,请使用lateinit,否则使其为空

答案 2 :(得分:0)

在Kotlin中,默认情况下,每个属性都必须初始化(即使具有null的可空类型)。

您可以直接在声明中或init块中初始化属性。

您的代码存在的问题是,启动函数可以在init块完成后继续运行,并且编译器知道这一点。所以,它告诉您-不要指望它。

如前所述,您可以使用 lateinit 来声明稍后将初始化属性,如果您确定该属性会在使用前发生。

答案 3 :(得分:-1)

weather必须使用该类的实例进行初始化,因为您没有说过它可以为null,并且您没有使用lateinit关键字(在这种情况下不应该使用)。 / p>

launch是一个异步协程调用,该调用会立即返回,但将来会在某个时候执行。这意味着您的init块可以完成并返回而无需初始化weather

改为使用runBlocking。这将阻塞,直到您将结果放在init块中为止,因此请确保实例化时天气不为null。像这样:

init {
    weather = runBlocking {
        weatherRepository.getWeather()
    }
}

您也可以将任何协程上下文分配器传递给runBlocking

或者-坚持使用协程,但是像下面这样加入init块:

init {
    val job = viewModelScope.launch {
        weather = weatherRepository.getWeather()
    }
    job.join()
}