Classe Handler!

No Android, a interface gráfica é gerenciada por uma Thread apenas.
Isso implica que não podemos atualizar um componente de uma tela em um código que estiver executando em uma outra thread.
Exemplificando, se existir uma Thread que realize uma conexão HTTP com o servidor e um requisito do software diz que um progress bar deve ser atualizado de acordo com as evoluções dessa requisição. Nesse cenário se atualizarmos o componente Progress Bar direto na Thread de requisição uma excessão ocorrerá, porque não podemos acessar componentes da interface gráfica por uma Thread que não seja a principal.

Detalhes da excessão:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Então, como podemos ter acesso a essa Thread principal?

A classe Handler é uma das respostas para essa pergunta. Ela permite através de diferentes maneiras, acesso a Thread principal da Interface Gráfica.
Vejamos um exemplo prático para descobrir algumas dessas maneiras. Crie um projeto Android no eclipse, e pode chama-lo de HandlerExample.
Nesse projeto, teremos uma Activity, um layout XML e uma classe que possui o nome de Processo. Essa última classe, como o nome indica, será utilizada para simular um processo de onde a interface gráfica será atualizada de acordo com o seu progresso.

Segue logo abaixo o código do Layout XML:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/text_view_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding_medium"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    
       
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Falha"
        android:onClick="onClickButtonFalha"
         />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Post"
        android:onClick="onClickHandlerPost"
         />
    
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Post Send Message"
        android:onClick="onClickHandlerPostSendMessage"
         />
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Post Delayed"
        android:onClick="onClickHandlerPostDelayed"
         />

     <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Run On UI"
        android:onClick="onClickRunOnUI"
         />

</LinearLayout>

Veja que no nosso layout esta presente 5 botões e 1 TextView, todos os botões deverão atualizar o TextView de um modo diferente mas utilizando recursos da classe Handler. Somente o primeiro botão tentará atualizar o TextView a partir de uma Thread para que possamos simular a excessão que ocorre quando tentamos atualizar um componente de interface gráfica através de uma Thread que não é a principal.

O segundo botão, que possui o texto Post, atualizará a interface gráfica através do método post da classe Handler. Esse método, recebe como parâmetro um Runnable que será executado assim que ele for acionado. Nesse Runnable poderemos colocar uma ação que altere os componentes da nossa tela.

O terceiro botão, que possui o texto Post Send Message, irá demonstrar como atualizar a tela a partir de mensagens (método sendMessage). Ou seja, deve-se passar a instância do seu Handler para algum processamento (Thread). Quando ocorrer uma evolução nesse processamento que seja necessário ser refletida na interface gráfica, o método sendMessage(Message msg) poderá ser acionado passando informações úteis colhidas do processamento para serem utilizadas no momento da atualização da tela. Nesse caso, a instância do Handler não precisa estar somente na Activity. É na Activity que se cria um objeto Handler, que por sua vez pode ser passado para uma classe que herde de Runnable por exemplo, ao executar o comando sendMessage a mensagem enviada será tratada pelo método handleMessage da própria classe Handler. Esse método pode ser sobrescrito, no momento da criação da classe Handler na Activity. De modo que ele poderá ter acesso aos componentes gráficos da tela e por fim atualiza-los (esse trecho parece confuso, mas ao examinar o código da Activity verá que é bem simples).

O quarto botão, que possui o texto Post Delayed, fara uso do método postDelayed(Runnable r, long time). Esse método, executará um Runnable após um intervalo de tempo (em mili segundos), tanto o Runnable quanto o intervalo de tempo serão passados como parâmetro.

Por último, temos o botão com o texto Run On UI, que utiliza o método runOnUiThread(Runnable r) da própria Activity. Esse método se trata de um atalho utilizado em situações onde o desenvolvedor não deseja manter um objeto Handler na sua Activity, mas tem a necessidade de atualizar a interface gráfica em algum momento.

A nossa tela ficará assim:

Todos os métodos da classe Handler que foram utilizados foram descritos. Então, agora é um bom momento para vê-los na nossa Activity. Mas antes, retornando ao layout.xml logo acima. Podemos ver que os Buttons que foram criados não possuem id’s de modo que conseguimos interceptar a ação de click na interface gráfica graças a tag onClick onde passamos o nome do método que será acionado quando o botão for clicado. Veja que cada um dos Buttons do nosso layout possui um onClick com o nome do seu respectivo método. É um jeito simples de gerenciar as ações dos botões, além de deixar o código muito mais limpo, uma vez que não terá que chamar o método findViewById para cada botão e nem terá que manter uma variável para a mesma.

public class MainActivity extends Activity {

	private TextView textView = null;


	private Handler handler = new Handler() {

		@Override
		public void handleMessage(Message msg) {

			Bundle param = msg.getData();
			int action = param.getInt(ActionCode.ACTION);

			switch (action) {
			
			case ActionCode.ACTION_FEEDBACK_PROCESS:
				int value = param.getInt(ActionCode.VALUE);
				textView.setText("Iteração: " + value);
				break;
				
			default:
				break;
			}

		}

	};

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

	public void onClickHandlerPost(View view) {
		handler.post(new AtualizarTextView("Post"));
	}

	public void onClickHandlerPostSendMessage(View view) {
		Processo processo = new Processo(handler);
		Thread t = new Thread(processo);
		t.start();
	}

	public void onClickHandlerPostDelayed(View view) {
		handler.postDelayed(new AtualizarTextView("Post Delayed"), 60000);
	}
	
	public void onClickRunOnUI(View v) {
		runOnUiThread(new AtualizarTextView("Run on UI!"));
	}
	
	public void onClickButtonFalha(View v) {
		Thread t = new Thread(new AtualizarTextView("Opa"));
		t.start();
	}

	private class AtualizarTextView implements Runnable {

		private String text;

		public AtualizarTextView(final String text) {
			this.text = text;
		}

		@Override
		public void run() {

			textView.setText(text);
		}

	}

}

O único detalhe dessa classe é que ela possui uma inner class chamada de AtualizarTextView que implementa Runnable e representará a ação que irá atualizar um componente da nossa interface gráfica. Conforme vimos anteriormente, se essa tarefa (AtualizarTextView) for executada sem a presença de um Handler, uma excessão ocorrerá (veja o botão um).

Outro ponto que chama a atenção é a implementação do método onClickHandlerPostSendMessage, veja que ele instancia um objeto do tipo Processo e passa como parâmetro para o seu construtor um objeto do tipo Handler. O Handler que foi passado, foi criado no topo da nossa Activity, e podemos ver que sobrescrevemos o método handleMessage do mesmo. Isso implica que, todas as vezes que o nosso objeto Handler acionar o método sendMessage, o método handleMessage será acionado recebendo valores passados pelo método sendMessage. O que chama a atenção é que o método handleMessage esta sobrescrito na nossa Activity onde o Handler foi chamado, e o sendMessage será chamado dentro da classe Processo. Desse modo conseguiremos enviar evoluções de um certo processamento para a nossa Activity, para manter uma barra de progresso atualizada por exemplo. O ponto negativo é que a classe de processamento ficará altamente acoplada a classe Handler, por isso, cabe ao desenvolvedor pensar nessa desvantagem ao utilizar esse estratégia e decidir se vale a pena ou não.
Logo abaixo segue a implementação da classe Processo.

public class Processo implements Runnable {
	
	Handler handler;

	public Processo(Handler handler) {
		this.handler = handler;
	}

	@Override
	public void run() {
		
		try {
			
			for(int i = 0; i < 3; i++) {
				Thread.sleep(1000);
				sendHandlerMessage(i);
			}	
			
		} catch(Exception e) {
			e.printStackTrace();
		}
		
	}
	
	private void sendHandlerMessage(int value) {
		Message msg = new Message();
		Bundle bundle = new Bundle();
		bundle.putInt(ActionCode.ACTION, ActionCode.ACTION_FEEDBACK_PROCESS);
		bundle.putInt(ActionCode.VALUE, value);
		msg.setData(bundle);
		handler.sendMessage(msg);
	}
	
}

Concluímos aqui mais um artigo de uma importante classe da estrutura do Android.

Anúncios

  1. Game em Canvas no Android! | Android On Board

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: