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

244
Visualizações
Android Kotlin Coroutines Freeze

I have come across an interesting coroutines freeze that I have simplified into the following problem:

//running on main thread
runBlocking {
   lifecycleScope.launch {
      delay(1000)
   }.join()
}

This causes the main thread to freeze indefinitely. I assume it is because of the following sequence of events:

  1. Queue to launch
  2. Call to join, pass main thread to coroutine pool
  3. Call to launch
  4. Call to delay, pass main thread to coroutine pool
  5. Thread moves back to join and waits
  6. Delay never finishes because it does not have a thread available?

Correct me if I am misunderstanding the above logic. What is a reasonable pattern to avoid this from happening? I understand the running blocking on the main thread is not a good idea, but deeper in the code it seems odd that you can accidentally freeze a single thread coroutine in this manner.

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

0

It's because of calling runBlocking on the main thread (which defeats the idea of even using coroutines); the order of events might not matter, when the top-most instruction already stalls the thread. It's always GlobalScope vs. CoroutineScope vs. lifecycleScope ...where lifecycleScope.launch can be used with different dispatchers:

  • lifecycleScope.launch(Dispatchers.IO): Launches a coroutine within the lifecycleScope provided by AndroidX. Coroutine gets cancelled as soon as lifecycle is invalidated (i.e. user navigates away from a fragment). Uses Dispatchers.IO as thread pool.

  • lifecycleScope.launch: Same as above, but uses Dispatchers.Main if not specified.

Therefore I'd assume, the behavior also may stem from dispatching with Dispatchers.Main.

over 4 years ago · Santiago Trujillo Relatório

0

It is even simpler than you think. Because of runBlocking(), join() doesn't return the thread to the event loop, so the launch() block never starts executing - deadlock.

Actually... this is not entirely true. join() returns the thread to the pool, but not to the one we think about. runBlocking() starts its own event loop using the caller thread. From the outside of runBlocking() the thread seems to be constantly blocked, but in the inside it loops and can suspend. Anyway, from the perspective of lifecycleScope the main thread is blocked and it can't launch anything on it.

What is a reasonable pattern to avoid this from happening?

Do not call runBlocking() on the main thread. Coroutines are no exception here. We should not run blocking IO or other kind of blocking operations on the main thread and that includes runBlocking().

over 4 years ago · Santiago Trujillo Relatório

0

Here's an explanation about what exactly causes the deadlock.

Any code anywhere in your app that is running on the Main thread is actually running from a message that has been sent to the Main Looper's queue of messages to process on the main thread.

The way Dispatchers.Main works is that it essentially sends pieces of coroutines as Runnable messages to an Android Handler that is backed by the Main Looper. Messages sent to the Main Looper can only be processed one at a time.

Inside your runBlocking call, your join() call is suspending until its associated coroutine finishes. That coroutine has been submitted to the main Looper. The Looper cannot process any messages in its queue until the current message returns. The current message is whichever one ran the method on the main thread that you called runBlocking from.

runBlocking is waiting on join() to return. join() is waiting for its coroutine to get processed by the Looper. The Looper is waiting on runBlocking to return.

I saw you mentioned in a comment that it works with GlobalScope. This is because GlobalScope uses Dispatchers.Default and lifecycleScope uses Dispatchers.Main (unless you modify the default context when launching the coroutine).

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