MutableLiveData:无法在协程的后台线程上调用 setValue
MutableLiveData: Cannot invoke setValue on a background thread from Coroutine
我正在尝试从协程触发 LiveData 更新:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.value = getAddressList()
}
return AddressList
}
但我收到以下错误:
IllegalStateException: Cannot invoke setValue on a background thread
有没有办法让它与协程一起工作?
我刚刚发现可以使用 withContext(Dispatchers.Main){}
:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
GlobalScope.launch {
withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
}
return AddressList
}
您可以执行以下操作之一:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.postValue(getAddressList())
}
return AddressList
}
或
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
val adresses = getAddressList()
withContext(Dispatchers.Main) {
AddressList.value = adresses
}
}
return AddressList
}
虽然其他人指出,在这种情况下,库提供了自己的方法来 post 对主线程的操作,但协程提供了一个通用的解决方案,无论给定库的功能如何。
第一步是停止对后台作业使用 GlobalScope
,这样做会导致泄漏,您的 activity、预定作业或您从中调用它的任何工作单元可能会泄漏被销毁,但你的工作将在后台继续,甚至将其结果提交到主线程。 official documentation on GlobalScope
的内容如下:
Application code usually should use application-defined CoroutineScope, using async or launch on the instance of GlobalScope is highly discouraged.
您应该定义自己的协程作用域,它的 coroutineContext
属性 应该包含 Dispatchers.Main
作为调度程序。此外,在函数调用和 returning LiveData
(基本上是另一种 Future
)中启动作业的整个模式并不是使用协程的最方便方式。相反,你应该
suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
并且在调用站点你应该 launch
一个协同程序,你现在可以在其中自由调用 getAddresses()
就好像它是一个阻塞方法一样并直接获取地址作为 return值。
在我的例子中,我必须将 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)
GlobalScope.launch {
delay(1000) // 1 sec delay
// do some background task
withContext(Dispatchers.Main) {
// call to UI thread
}
}
使用 liveData.postValue(value)
而不是 liveData.value = value
。它称为异步。
postValue - Posts a task to a main thread to set the given value.
我在为测试用例调用 runBlockingTest 中的协程时遇到了这个错误
TestCoroutineRule().runBlockingTest {
}
我通过将 instantExecutorRule 添加为 class 成员来修复它
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
以下代码适用于我从线程更新实时数据:
val adresses = getAddressList()
GlobalScope.launch {
messages.postValue(adresses)
}
我正在尝试从协程触发 LiveData 更新:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.value = getAddressList()
}
return AddressList
}
但我收到以下错误:
IllegalStateException: Cannot invoke setValue on a background thread
有没有办法让它与协程一起工作?
我刚刚发现可以使用 withContext(Dispatchers.Main){}
:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
GlobalScope.launch {
withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
}
return AddressList
}
您可以执行以下操作之一:
object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
AddressList.postValue(getAddressList())
}
return AddressList
}
或
fun getAddressesLiveData(): LiveData<List<Address>> {
AddressList.value = listOf()
GlobalScope.launch {
val adresses = getAddressList()
withContext(Dispatchers.Main) {
AddressList.value = adresses
}
}
return AddressList
}
虽然其他人指出,在这种情况下,库提供了自己的方法来 post 对主线程的操作,但协程提供了一个通用的解决方案,无论给定库的功能如何。
第一步是停止对后台作业使用 GlobalScope
,这样做会导致泄漏,您的 activity、预定作业或您从中调用它的任何工作单元可能会泄漏被销毁,但你的工作将在后台继续,甚至将其结果提交到主线程。 official documentation on GlobalScope
的内容如下:
Application code usually should use application-defined CoroutineScope, using async or launch on the instance of GlobalScope is highly discouraged.
您应该定义自己的协程作用域,它的 coroutineContext
属性 应该包含 Dispatchers.Main
作为调度程序。此外,在函数调用和 returning LiveData
(基本上是另一种 Future
)中启动作业的整个模式并不是使用协程的最方便方式。相反,你应该
suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
并且在调用站点你应该 launch
一个协同程序,你现在可以在其中自由调用 getAddresses()
就好像它是一个阻塞方法一样并直接获取地址作为 return值。
在我的例子中,我必须将 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)
GlobalScope.launch {
delay(1000) // 1 sec delay
// do some background task
withContext(Dispatchers.Main) {
// call to UI thread
}
}
使用 liveData.postValue(value)
而不是 liveData.value = value
。它称为异步。
postValue - Posts a task to a main thread to set the given value.
我在为测试用例调用 runBlockingTest 中的协程时遇到了这个错误
TestCoroutineRule().runBlockingTest {
}
我通过将 instantExecutorRule 添加为 class 成员来修复它
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
以下代码适用于我从线程更新实时数据:
val adresses = getAddressList()
GlobalScope.launch {
messages.postValue(adresses)
}