Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

332
Visualizações
MutableLiveData: Cannot invoke setValue on a background thread from Coroutine

I'm trying to trigger an update on LiveData from a coroutine:

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

but I get the following error:

IllegalStateException: Cannot invoke setValue on a background thread

Is there a way to make it work with coroutines?

over 4 years ago · Santiago Trujillo
7 Respostas
Responde à pergunta

0

I was facing this error when I called a coroutine inside runBlockingTest for a test case

TestCoroutineRule().runBlockingTest {

}

I got it fixed by adding the instantExecutorRule as a class member

@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
over 4 years ago · Santiago Trujillo Relatório

0

I just figured out that it's possible by using withContext(Dispatchers.Main){}:

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    GlobalScope.launch {
        withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
    }
    return AddressList
}
over 4 years ago · Santiago Trujillo Relatório

0

You can do one of the following :

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

return AddressList
}

or

fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        val adresses = getAddressList()
        withContext(Dispatchers.Main) {
            AddressList.value = adresses
        }
    }
    return AddressList
}
over 4 years ago · Santiago Trujillo Relatório

0

Although others have pointed out that, in this case, the library provides its own method to post an operation to the main thread, coroutines provide a general solution that works regardless of a given library's functionality.

The first step is to stop using GlobalScope for background jobs, doing this will lead to leaks where your activity, or scheduled job, or whatever unit of work you invoke this from, may get destroyed, and yet your job will continue in the background and even submit its results to the main thread. Here's what the official documentation on GlobalScope states:

Application code usually should use application-defined CoroutineScope, using async or launch on the instance of GlobalScope is highly discouraged.

You should define your own coroutine scope and its coroutineContext property should contain Dispatchers.Main as the dispatcher. Furthermore, the whole pattern of launching jobs within a function call and returning LiveData (which is basically another kind of Future), isn't the most convenient way to use coroutines. Instead you should have

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

and at the call site you should launch a coroutine, within which you can now freely call getAddresses() as if it was a blocking method and get the addresses directly as a return value.

over 4 years ago · Santiago Trujillo Relatório

0

If you want to updated UI by using Coroutines, there are 2 ways to achieve this

GlobalScope.launch(Dispatchers.Main):

GlobalScope.launch(Dispatchers.Main) {
    delay(1000)     // 1 sec delay
    // call to UI thread
}

And if you want some work to be done in background but after that you want to update UI, this can be achieved by the following:

withContext(Dispatchers.Main)

GlobalScope.launch {
    delay(1000)     // 1 sec delay

    // do some background task

    withContext(Dispatchers.Main) {
            // call to UI thread
    }
}
over 4 years ago · Santiago Trujillo Relatório

0

Use liveData.postValue(value) instead of liveData.value = value. It is called asynchronous.

From documentation:

postValue - Posts a task to a main thread to set the given value.

over 4 years ago · Santiago Trujillo Relatório

0

In my case, I had to add Dispatchers.Main to the launch arguments and it worked fine:

 val job = GlobalScope.launch(Dispatchers.Main) {
                    delay(1500)
                    search(query)
                }
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda