代码之家  ›  专栏  ›  技术社区  ›  kike

mutablelivedata:无法在来自coroutine的后台线程上调用setvalue

  •  2
  • kike  · 技术社区  · 6 年前

    我正试图从一个协程触发livedata的更新:

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

    但我得到了以下错误:

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

    有没有办法让它和郊游一起工作?

    3 回复  |  直到 5 年前
        1
  •  1
  •   kike    6 年前

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

    object AddressList: MutableLiveData<List<Address>>()
    fun getAddressesLiveData(): LiveData<List<Address>> {
        GlobalScope.launch {
            withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
        }
        return AddressList
    }
    
        2
  •  1
  •   pdegand59    6 年前

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

    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
    }
    
        3
  •  1
  •   Marko Topolnik    6 年前

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

    第一步是停止使用 GlobalScope 对于后台作业,执行此操作将导致泄漏,其中您的活动、计划作业或从中调用此操作的任何工作单元可能会被破坏,但是您的作业将在后台继续,甚至将其结果提交给主线程。以下是 official documentation on GlobalScope 国家:

    应用程序代码通常应该使用应用程序定义的coroutinescope,强烈建议在globalscope实例上使用async或launch。

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

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

    在呼叫点你应该 launch 一次联程,你现在可以在其中自由调用 getAddresses() 就好像它是一个阻塞方法,直接获取地址作为返回值。