Android: Atualizando a interface gráfica com o Handler

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (2)  (0)

Veja neste artigo um dos modos de atualizar a interface gráfica, em aplicações Java para Android, fora do escopo atual utilizando a classe Handler.


Introdução

O Handler é uma classe do pacote android.os, como o próprio nome já diz ele é um “Manipulador”. Quando você cria um novo Handler, ele é destinado à fila de mensagens/threads do thread que o criou. A partir daí toda mensagem que for destinada a ele será entregue para aquela fila de mensagens para serem executadas. Resumidamente ele fica responsável por entregar as mensagens para a fila de sua thread, executando-as em seguida.

Ele pode ser utilizado de duas maneiras: (1) para agendar mensagens e Threads a serem executadas e (2) para enfileirar uma ação a ser executada em um segmento diferente do qual foi enviada. Neste artigo vamos abordar somente a segunda maneira.

A seguir vamos mostrar quando e porque utilizar o Handler.

Neste caso, foi criado um Toast dentro de uma thread na “MainActivity” porém o aplicativo foi fechado inesperadamente causando o erro mostrado na figura 1 (“java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare().”).

Erro retirado do DDMS

Figura 1: Erro retirado do DDMS

Abaixo temos o código que causou tal erro:

Listagem 1: Exemplo de uma exceção “java.lang.RuntimeException”

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        new Thread() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Teste de erro", Toast.LENGTH_LONG).show();
            }
        }.start();
    }

O problema foi tentar inserir uma informação na Thread da interface gráfica dentro de uma outra Thread, fugindo do escopo atual. Ao decorrer deste artigo vamos mostrar como corrigir esses tipos de problemas com a classe Handler.

Codificando

No início, temos de entender mais algumas coisas do Handler, por exemplo, sua estrutura, como construí-lo e o objeto Message, que vai ser responsável pela interação da thread de processamento com ele.

Quando construirmos o Handler vamos sobrescrever o método handleMessage(Message msg) que será responsável pelo tratamento do objeto Message enviado pelo método sendMessage(Message msg) da própria classe.

Listagem 2: Construção e configuração do Handler

        //construção e configuração do handler 
       Handler handler = new Handler() {
            
            @Override
            public void handleMessage(Message msg) {
                //chamo um método para o tratamento da mensagem
                //e melhor organização do código.
                updateUI(msg);
            }
        };
 

A classe Message “é como se fosse” um Intent, porém com uma grande diferença, ela permite a transição de qualquer objeto, pois contém um atributo público do tipo Object, dois int “arg1” e “arg2” e além de um atributo privado Bundle que pode ser utilizado com os métodos getData() e setData().

Observação: para enviarmos o Message é muito importante definir o atributo público “what”, que vai permitir o reconhecimento da mensagem. Esta classe ainda permite vários outros tipos de controle que não vamos abranger neste artigo, mas podem ser muito úteis em programas mais complexos.

Antes de mostrar o tratamento do objeto Message, construiremos a Thread que vai disparar a mensagem para o Handler. A Thread foi criada em uma nova classe e nomeada de ThreadProcessamento, que deverá receber como parâmetro o objeto Handler, pois é com ele que vamos disparar as mensagens. No método run() da thread contém um laço que simula um processamento e a cada volta é disparada uma mensagem através do Handler. Essa mensagem será interceptada no método handleMessage(), que vimos a pouco, para que seja feito o tratamento adequado.

Listagem 3: Contrução da ThreadProcessamento

public class ThreadProcessamento extends Thread {

    private Handler handler;

    public ThreadProcessamento(Handler handler) {
        this.handler = handler;
    }
    
    @Override
    public void run() {

        for (int i = 0; i < 10; i++) {
            Message message = new Message();
            //defino um codigo para controle.
            message.what = 1;

            //aqui posso passar qualquer objeto. 
            //No caso estou passando uma String
            if (i < 9) {
                message.obj = "Contador: " + i;
            } else {
                message.obj = "Finalizando...";
            }

            //Envio da mensagem.
            handler.sendMessage(message);

            try {
                //simula processamento de 1seg
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //Note que mudei o código de controle.
        //O controle dessa mensagem será diferente.
        Message message = new Message();
        message.what = 2;
        handler.sendMessage(message);
        
    }
}

Agora que foi feita a Thread de processamento, criaremos a classe MainActivity por partes, ela vai ter dois atributos básicos para mostrar como exemplo. O primeiro é um TextView que foi definido em nosso layout com o id “texto” e o segundo é o Handler, ambas vão ser carregadas no método onCreate(), que também vai construir e iniciar a ThreadProcessamento.

Listagem 4: Método onCreate da MainActivity

public class MainActivity extends Activity {

    private TextView textView;
    private Handler handler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textView = (TextView) findViewById(R.id.texto);

        //chamo um método para o tratamento da mensagem
        //e melhor organização do código.
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                //chamo um método para melhor organização.
                updateUI(msg);
            }
        };


        //Thread responsável pelo processamento de dados.
        //O handler é passado para que sejá possível atualizar a tela.
        ThreadProcessamento threadProcessamento = new ThreadProcessamento(handler);
        threadProcessamento.start();
    }

[...]//aqui entra o método updateUI().

}

Com o que foi mostrado até agora somos capazes de iniciar uma thread e enviar uma mensagem para que nosso Handler coloque-a na fila de processamento de nossa thread gráfica, evitando assim o erro que foi mostrado em nossa introdução.

Para concluir, vamos tratar o objeto Message que foi enviado pela ThreadProcessamento. O tratamento é bem simples, comparamos o atributo “what” com o código que definimos e usamos o que foi passado como parâmetro dentro do objeto Message para atualizar a interface gráfica. Temos dois casos, no primeiro só é alterado o texto do TextView e no segundo será exibido um Toast e a Activity será finalizada.

Listagem 5: Tratamento do objeto Message pelo método updateUI()

    /**
     * Método responsável pelo controle de Message do Handler
     *
     * @param msg Message
     */
    private void updateUI(Message msg) {
        if (msg.what == 1) {
            //Converto o object para string (pois foi o que eu passei)
            String texto = (String) msg.obj;
            //defino no meu TextView o texto.
            textView.setText(texto);

        } else if (msg.what == 2) {
            //finalizo a activity
            Toast.makeText(this, "Android Handler - DevMedia", Toast.LENGTH_LONG).show();
            finish();
        }
    }

Conclusão

Apesar de ser um conceito diferente de algumas plataformas, é muito interessante como o Android trabalha com sua pilha de processamento utilizando o Handler. Esta classe é muito utilizada no caso aplicado neste artigo e também em outras situações específicas, se comportando muito bem para empilhar atualizações da interface gráfica e gerenciá-las, pois é simples e não exige muito processamento se bem utilizado.

Com isso finalizo esse artigo. O projeto que foi utilizado como exemplo poderá ser obtido através da opção “código fonte” no topo dessa página. Até o próximo, um grande abraço e obrigado.

Referências

 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Receba nossas novidades
Ficou com alguma dúvida?