MutableLiveData:无法在协程的后台线程上调用setValue

kik*_*ike 5 kotlin android-livedata kotlin-coroutines

我正在尝试从协程触发LiveData更新:

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        AddressList.value = getAddressList()
    }
    return AddressList
}
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

IllegalStateException:无法在后台线程上调用setValue

有没有办法使它与协程一起工作?

Ami*_*emi 227

使用liveData.postValue(value)代替liveData.value = value。它被称为异步。

文档

postValue - 将任务发布到主线程以设置给定值。


pde*_*d59 18

您可以执行以下操作之一:

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        AddressList.postValue(getAddressList())
    }

return AddressList
}
Run Code Online (Sandbox Code Playgroud)

或者

fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        val adresses = getAddressList()
        withContext(Dispatchers.Main) {
            AddressList.value = adresses
        }
    }
    return AddressList
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,我认为这是完美的`postValue()` 使用场景。不需要强制主线程上下文。 (7认同)

Mar*_*nik 6

尽管其他人指出,在这种情况下,库提供了自己的方法来将操作发布到主线程,但协程提供了一种通用的解决方案,无论给定库的功能如何,该解决方案都可以工作。

第一步是停止使用GlobalScope后台作业,这样做会导致泄漏,您的活动、计划作业或您从中调用它的任何工作单元可能会被破坏,但您的作业将在后台继续,甚至将其结果提交给主线程。以下是官方文档的GlobalScope说明:

应用程序代码通常应使用应用程序定义的 CoroutineScope,强烈建议不要在 GlobalScope 实例上使用 async 或 launch。

您应该定义自己的协程范围,并且其coroutineContext属性应包含Dispatchers.Main为调度程序。此外,在函数调用中启动作业并返回LiveData(这基本上是另一种Future)的整个模式并不是使用协程的最方便的方法。相反你应该有

suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
Run Code Online (Sandbox Code Playgroud)

在调用站点,您应该有launch一个协程,在其中您现在getAddresses()可以像阻塞方法一样自由调用,并直接获取地址作为返回值。


kik*_*ike 5

我刚刚发现使用withContext(Dispatchers.Main){}以下方法是可能的:

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    GlobalScope.launch {
        withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
    }
    return AddressList
}
Run Code Online (Sandbox Code Playgroud)

  • 不要在 `withContext(Dispatchers.Main)` 块内调用 `getAdresseList()`,因为这将在主线程上运行。在外部调用它,以便它由`Dispatchers.Default` 正确调度,并且只使用主线程上的结果 (2认同)
  • 您只需将繁重的任务返回到 UI 线程中运行即可 (2认同)