134

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

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

但我收到以下错误:

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

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

4

8 回答 8

287

使用liveData.postValue(value)而不是liveData.value = value. 它被称为异步。

文档

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

于 2020-02-08T12:03:39.187 回答
21

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

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
}
于 2018-11-14T16:28:06.797 回答
6

我刚刚发现可以通过使用withContext(Dispatchers.Main){}

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    GlobalScope.launch {
        withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
    }
    return AddressList
}
于 2018-11-14T16:20:33.230 回答
4

如果您想使用Coroutines更新 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
    }
}
于 2019-12-18T15:46:58.060 回答
4

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

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

应用程序代码通常应该使用应用程序定义的 CoroutineScope,强烈建议不要在 GlobalScope 的实例上使用异步或启动。

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

suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }

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

于 2018-11-15T10:13:59.220 回答
3

就我而言,我必须将Dispatchers.Main添加到启动参数中,并且效果很好:

 val job = GlobalScope.launch(Dispatchers.Main) {
                    delay(1500)
                    search(query)
                }
于 2019-08-21T13:34:16.250 回答
0

当我在 runBlockingTest 中为测试用例调用协程时,我遇到了这个错误

TestCoroutineRule().runBlockingTest {

}

我通过将 InstantExecutorRule 添加为类成员来修复它

@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
于 2021-07-28T12:18:33.143 回答
0

下面的代码对我有用,可以从线程更新 livedata:

 val adresses = getAddressList()
 GlobalScope.launch {
     messages.postValue(adresses)
 }
于 2022-03-03T05:13:40.677 回答