Kotlin MutableLiveData:无法从协同例程在后台线程上调用setValue
我正在尝试从协同程序触发LiveData更新:Kotlin MutableLiveData:无法从协同例程在后台线程上调用setValue,kotlin,android-livedata,kotlin-coroutines,Kotlin,Android Livedata,Kotlin Coroutines,我正在尝试从协同程序触发LiveData更新: object AddressList: MutableLiveData<List<Address>>() fun getAddressesLiveData(): LiveData<List<Address>> { AddressList.value = listOf() GlobalScope.launch { AddressList.value = getAddres
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.value = getAddressList()
}
return AddressList
}
对象地址列表:MutableLiveData()
fun getAddressesLiveData():LiveData{
AddressList.value=listOf()
环球镜发射{
AddressList.value=getAddressList()
}
回信地址表
}
但我得到了以下错误:
IllegalStateException:无法在后台线程上调用setValue
有没有一种方法可以让它与协同程序一起工作?我刚刚发现,通过使用
和上下文(Dispatchers.Main){}
是可能的:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
GlobalScope.launch {
withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
}
return AddressList
}
对象地址列表:MutableLiveData()
fun getAddressesLiveData():LiveData{
环球镜发射{
withContext(Dispatchers.Main){AddressList.value=getAddressList()}
}
回信地址表
}
您可以执行以下操作之一:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.postValue(getAddressList())
}
return AddressList
}
对象地址列表:MutableLiveData()
fun getAddressesLiveData():LiveData{
AddressList.value=listOf()
环球镜发射{
AddressList.postValue(getAddressList())
}
回信地址表
}
或
fun getAddressesLiveData():LiveData{
AddressList.value=listOf()
环球镜发射{
val address=getAddressList()
withContext(Dispatchers.Main){
AddressList.value=地址
}
}
回信地址表
}
尽管其他人已经指出,在这种情况下,库提供了自己的方法将操作发布到主线程,但协程提供了一种通用的解决方案,无论给定库的功能如何,都能正常工作
第一步是停止在后台作业中使用GlobalScope
,这样做会导致泄漏,您的活动、计划作业或您调用它的任何工作单元可能会被破坏,而您的作业将在后台继续,甚至将其结果提交给主线程。以下是各州的情况:
应用程序代码通常应该使用应用程序定义的CoroutineScope,强烈建议在GlobalScope实例上使用async或launch
您应该定义自己的协同程序作用域,其coroutineContext
属性应该包含Dispatchers.Main
作为调度器。此外,在函数调用中启动作业并返回LiveData
(这基本上是另一种未来
)的整个模式并不是使用协同路由的最方便方式。相反,你应该这样做
suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
在调用站点,您应该
启动一个协同程序,您现在可以在其中自由调用getAddresses()
,就像它是一个阻塞方法一样,并直接获取地址作为返回值。在我的例子中,我必须在启动参数中添加Dispatchers.Main,效果很好:
val job = GlobalScope.launch(Dispatchers.Main) {
delay(1500)
search(query)
}
如果您想通过使用协同程序更新UI,有两种方法可以实现这一点
GlobalScope.launch(Dispatchers.Main):
GlobalScope.launch(Dispatchers.Main) {
delay(1000) // 1 sec delay
// call to UI thread
}
如果您希望在后台完成一些工作,但之后您希望更新UI,可以通过以下方式实现:
withContext(Dispatchers.Main)
使用liveData.postValue(value)
而不是liveData.value=value
。它被称为异步
发件人:
postValue-将任务发布到主线程以设置给定值
你不想使用postValue有什么原因吗?@RedDeckWins。不,我只是没有考虑到这一点,但这绝对是从协同程序更新可变LiveData的方法。不要在withContext(Dispatchers.Main)
块中调用getAddressList()
,因为这将在主线程上运行。在外部调用它,以便它由分派器正确分派。默认设置为并仅在主线程上使用结果您只需返回繁重的任务以在UI线程中运行谢谢@kikey实际上,我认为这是完美的postValue()
使用场景。不需要强制主线程上下文。
GlobalScope.launch(Dispatchers.Main) {
delay(1000) // 1 sec delay
// call to UI thread
}
GlobalScope.launch {
delay(1000) // 1 sec delay
// do some background task
withContext(Dispatchers.Main) {
// call to UI thread
}
}