Threads, Handler, Looper e Message Queue: Parte 2

Jeziel Lago
Android Dev BR
Published in
4 min readFeb 9, 2021

Continuando a conversa👨🏻‍💻

Começamos a falar desse tema na Parte 1 — Threads. Se você ainda não leu, recomendo conferir lá 😁👍🏻.

Handler

Um Handler nos permite enviar e processar objetos Message e Runnable associados à uma fila de mensagens de uma Thread.

Cada instância do Handler está associada a uma única thread e à fila de mensagens dessa thread. Quando você cria um novo Handler, ele é vinculado a thread que o criou. Daí em diante ele entregará Message e Runnable para essa fila de mensagens e os executará assim que saírem da fila de mensagens.

Existem dois usos principais para um Handler:

  • Agendar mensagens e runnables para serem executados em algum momento no futuro;
  • Enfileirar uma ação a ser executada em uma thread diferente da sua.

Ao enviar uma mensagem para um Handler, você pode permitir que ela seja processada assim que a fila de mensagens estiver pronta para isso ou pode também especificar um delay antes de ser processada ou tempo absoluto para que seja processada.

Relação Handler — Main Thead

Quando um processo é criado para o app, a main thread é dedicada a executar uma fila de mensagens que cuida do gerenciamento dos objetos top-level da aplicação (activities, broadcast receivers, services, …). Você pode criar suas próprias threads e se comunicar com a main thread do app por meio de um Handler. Isso é feito chamando os mesmos métodos post ou sendMessage, mas a partir da sua nova thread. O Runnable ou Message fornecido será então agendado na fila de mensagens do Handler e processado quando apropriado.

Looper

É uma classe usada para executar loop de mensagem para uma thread. As threads por padrão não tem um loop de mensagem associado a ela. Existem dois métodos fundamentais para utilizar um Looper em uma thread:

  • Looper.prepare(): Inicializa a thread atual como um looper. Isso lhe dá a chance de criar Handler's que fazem referência a esse looper, antes de realmente iniciar o loop.
  • Looper.loop(): Executa a fila de mensagem para a thread atual. Para encerrar o loop, basta chamar o quit.
  • Looper.myLoop(): Devolve o looper que foi criado e associado a thread atual.

Looper, loop, parece tudo muito confuso com esses nomes 🤯, mas existe uma explicação. O Looper é uma classe que cria um loop infinito (imagine um while(true) {...} ou um for(;;)), para processar mensagens em uma fila. Agora os nomes fazem todo sentido, não acha? 😅

A maior parte das interações que um Looper tem é com um Handler.

Exemplo: Thread — Handler — Looper

Aqui está um exemplo clássico de uma implementação de uma thread que usa um Looper (com prepare e loop) para criar um Handler que se comunica com o Looper.

MessageQueue

É uma classe que contém uma lista de mensagens para serem entregues a um Looper. As mensagens não são adicionadas na MessageQueue. Isso acontece através de um Handler associado ao Looper.

Para tornar mais claro nosso entendimento a respeito de Handler, Looper e MessageQueue vamos analisar o diagrama a seguir:

  • O Handler envia mensagens para a MessageQueue (post, sendMessage, postDelayed, …);
  • O Looper solicita mensagens para a MessageQueue;
  • A mensagem recebida pelo Looper é enviada para o Handler processar (ele recebe essa mensagem em um Handler.Callback).

Handler's e mensagens em diferentes threads

Imagine que você quer criar uma thread para realizar algum tipo de processamento, alguma operação de I/O que não é permitida na main thread ou qualquer outra coisa que pode de alguma forma bloquear a execução da main thread. Porém, após o processamento, você gostaria de retornar para a main thread com o resultado.

Podemos criar uma thread com um Looper e um Handler para receber uma mensagem, processá-la e enviar o resultado ao finalizar o processamento.

No exemplo acima (ilustrativo apenas), criamos uma thread com um Looper e um Handler que recebe uma url de uma imagem e devolve um path de um arquivo após baixar a imagem. Podemos ver que o resultado é enviado ao mainHandler para que seja recebido na main thread.

Aqui podemos ver uma das formas de criar um Handler que escuta mensagens na main thread. Podemos passá-lo para a nossa thread para receber os resultados do processamento em background da ImageFileThread.

Vale ressaltar que nesse exemplo a thread não cancela ou é interrompida quando termina de processar uma mensagem. Em nenhum ponto chamamos Looper.quit() para encerrar o loop.

Os exemplos apresentados aqui são exclusivamente de propósito didático para compreender o funcionamento dessas ferramentas. Existem outras formas melhores de lidar com threading e assincronismo no Android (RxJava e Kotlin Coroutines, por exemplo).

Se você gostou deste post, não deixe de compartilhar com outros devs que você conhece! 🤗

Muito obrigado por ler até aqui!

--

--