Tutorial Android #9 – Ciclo de Vida da Activity

Padrão

Olá novamente! No último tutorial vimos como criar uma barra de progresso para carregar uma pseudo-atividade em nosso aplicativo. No post de hoje, vamos trabalhar um pouco com o ciclo de uma Activity. Vamos tornar a nossa enrolação tarefa um pouco mais longa, e fazer com que ela seja pausada ao alternar para outra aplicação e retomá-la ao voltar. Este padrão, pausando processamento quando a aplicação não está em primeiro plano, é bastante útil em diversas outras situações.

O primeiro passo é deixar a nossa tarefa longa o suficiente para que a nossa ideia de aplicativo funcione. Altere a nossa tarefaLonga, aumentando a quantidade de loopings:

private Runnable tarefaLonga = new Runnable() {
	public void run() {
		for (int i = progresso; i < 10000; i += 200) {
			fazerAlgoDemorado(500);
		}

		runOnUiThread(new Runnable() {
			public void run() {
				setProgressBarVisibility(false);
			}
		});
	}
};

O próximo passo é controlar para que a tarefa somente seja executada quando a aplicação estiver ativa. Não precisamos necessariamente interromper a thread durante a sua execução. O que precisamos, de fato, é sinalizar que o aplicativo está ativo ou não, e condicionar a execução da tarefa a isto. Podemos então criar um sinalizador para fazer isso. Como este sinalizador será utilizado por duas threads, ele precisa estar preparado para lidar com esta concorrência. Declare um membro chamado estaAtivo do tipo AtomicBoolean (pacote java.util.concurrent.atomic.AtomicBoolean).

EditText nome = null;
EditText endereco = null;
EditText anotacoes = null;
RadioGroup tipos = null;
int progresso = 0;
AtomicBoolean estaAtivo = new AtomicBoolean(true);

Continuando, vamos agora condicionar a execução do nosso laço for na nossa tarefaLonga a este sinalizador:

for (int i = progresso; i < 10000 && estaAtivo.get(); i += 200) {
	fazerAlgoDemorado(500);
}

Seguindo, vamos implementar o método onPause() para atualizar o status do sinalizador estaAtivo.

@Override
public void onPause() {
	super.onPause();
	estaAtivo.set(false);
}

Agora, precisamos restaurar a execução quando nosso aplicativo voltar ao primeiro plano. Primeiramente, vamos implementar o método onResume(), que atualizará o status do sinalizador estaAtivo.

@Override
public void onResume() {
	super.onResume();
	estaAtivo.set(true);

	if (progresso > 0) {
		iniciarTarefa();
	}
}

Como você pode perceber, criamos um método chamado iniciarTarefa(). Ele fará com que o processo de iniciar a thread fique mais organizado. Sua implementação é a seguinte:

private void iniciarTarefa() {
	setProgressBarVisibility(true);
	new Thread(tarefaLonga).start();
}

Agora, podemos alterar o método onOptionsItemSelected() para utilizar o método iniciarTarefa():

@Override
public boolean onOptionsItemSelected(MenuItem item) {
	if (item.getItemId() == R.id.anotacao) {
		String mensagem = "Nenhum restaurante selecionado";

		if (atual != null) {
			mensagem = atual.getAnotacoes();
		}

		Toast.makeText(this, mensagem, Toast.LENGTH_LONG).show();

		return true;
	} else if (item.getItemId() == R.id.rodar) {
		iniciarTarefa();

		return true;
	}

	return super.onOptionsItemSelected(item);
}

Por fim, precisamos tratar para que a barra de progresso não seja reinicializada nem ocultada quando a thread for interrompida pela inatividade da nossa Activity. Dessa forma, altere mais uma vez a nossa tarefaLonga:

private Runnable tarefaLonga = new Runnable() {
	public void run() {
		for (int i = progresso; i < 10000 && estaAtivo.get(); i += 200) {
			fazerAlgoDemorado(500);
		}

		if (estaAtivo.get()) {
			runOnUiThread(new Runnable() {
				public void run() {
					setProgressBarVisibility(false);
					progresso = 0;
				}
			});
		}
	}
};

Agora, já podemos testar a nossa aplicação. Execute-a e selecione a opção Executar Tarefa dentro do menu. Enquanto ela estiver sendo executada, pressione o botão de chamada do emulador ou telefone (verde), o que fará a tela de chamada ser exibida, resultando na nossa aplicação pausada. Em seguida, pressione a tecla voltar para que a tarefa seja retomada.

A seguir, a listagem completa da classe ListaRestaurantes.

package net.rafaeltoledo.restaurante;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import net.rafaeltoledo.restaurante.model.Restaurante;
import android.app.TabActivity;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.Toast;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;

public class ListaRestaurantes extends TabActivity {

	List<Restaurante> listaRestaurantes = new ArrayList<Restaurante>();
	AdaptadorRestaurante adaptador = null;
	Restaurante atual = null;

	EditText nome = null;
	EditText endereco = null;
	EditText anotacoes = null;
	RadioGroup tipos = null;
	int progresso = 0;
	AtomicBoolean estaAtivo = new AtomicBoolean(true);

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_PROGRESS);
		setContentView(R.layout.main);

		nome = (EditText) findViewById(R.id.nome);
		endereco = (EditText) findViewById(R.id.end);
		tipos = (RadioGroup) findViewById(R.id.tipos);

		Button salvar = (Button) findViewById(R.id.salvar);
		salvar.setOnClickListener(onSave);

		ListView lista = (ListView) findViewById(R.id.restaurantes);
		adaptador = new AdaptadorRestaurante();
		lista.setAdapter(adaptador);
		lista.setOnItemClickListener(onListClick);

		TabSpec descritor = getTabHost().newTabSpec("tag1");
		descritor.setContent(R.id.restaurantes);
		descritor.setIndicator("Lista", getResources().getDrawable(R.drawable.lista));
		getTabHost().addTab(descritor);

		descritor = getTabHost().newTabSpec("tag2");
		descritor.setContent(R.id.detalhes);
		descritor.setIndicator("Detalhes", getResources().getDrawable(R.drawable.restaurante));
		getTabHost().addTab(descritor);

		getTabHost().setCurrentTab(0);
	}

	private void fazerAlgoDemorado(final int incremento) {
		runOnUiThread(new Runnable() {
			public void run() {
				progresso += incremento;
				setProgress(progresso);
			}
		});

		SystemClock.sleep(250); // Pode ser algo mais útil!
	}

	private Runnable tarefaLonga = new Runnable() {
		public void run() {
			for (int i = progresso; i < 10000 && estaAtivo.get(); i += 200) {
				fazerAlgoDemorado(500);
			}

			if (estaAtivo.get()) {
				runOnUiThread(new Runnable() {
					public void run() {
						setProgressBarVisibility(false);
						progresso = 0;
					}
				});
			}
		}
	};

	@Override
	public void onPause() {
		super.onPause();
		estaAtivo.set(false);
	}

	@Override
	public void onResume() {
		super.onResume();
		estaAtivo.set(true);

		if (progresso > 0) {
			iniciarTarefa();
		}
	}

	private void iniciarTarefa() {
		setProgressBarVisibility(true);
		new Thread(tarefaLonga).start();
	}

	private OnItemClickListener onListClick = new OnItemClickListener() {
		public void onItemClick(AdapterView<?> parent, View view, int position,
				long id) {
			atual = listaRestaurantes.get(position);
			nome.setText(atual.getNome());
			endereco.setText(atual.getEndereco());
			anotacoes.setText(atual.getAnotacoes());

			if (atual.getTipo().equals("rodizio")) {
				tipos.check(R.id.rodizio);
			} else if (atual.getTipo().equals("fast_food")) {
				tipos.check(R.id.fast_food);
			} else {
				tipos.check(R.id.a_domicilio);
			}

			getTabHost().setCurrentTab(1);
		}
	};

	private OnClickListener onSave = new OnClickListener() {

		public void onClick(View arg0) {
			atual = new Restaurante();
			EditText nome = (EditText) findViewById(R.id.nome);
			EditText endereco = (EditText) findViewById(R.id.end);
			EditText anotacoes = (EditText) findViewById(R.id.anotacoes);

			atual.setNome(nome.getText().toString());
			atual.setEndereco(endereco.getText().toString());
			atual.setAnotacoes(anotacoes.getText().toString());

			RadioGroup tipos = (RadioGroup) findViewById(R.id.tipos);

			switch (tipos.getCheckedRadioButtonId()) {
			case R.id.rodizio:
				atual.setTipo("rodizio");
				break;
			case R.id.fast_food:
				atual.setTipo("fast_food");
				break;
			case R.id.a_domicilio:
				atual.setTipo("a_domicilio");
				break;
			}

			adaptador.add(atual);
		}
	};

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		new MenuInflater(this).inflate(R.menu.opcao, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == R.id.anotacao) {
			String mensagem = "Nenhum restaurante selecionado";

			if (atual != null) {
				mensagem = atual.getAnotacoes();
			}

			Toast.makeText(this, mensagem, Toast.LENGTH_LONG).show();

			return true;
		} else if (item.getItemId() == R.id.rodar) {
			iniciarTarefa();

			return true;
		}

		return super.onOptionsItemSelected(item);
	}

	class AdaptadorRestaurante extends ArrayAdapter<Restaurante> {
		AdaptadorRestaurante() {
			super(ListaRestaurantes.this, R.layout.linha,
					listaRestaurantes);
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			View linha = convertView;
			ArmazenadorRestaurante armazenador = null;

			if (linha == null) {
				LayoutInflater inflater = getLayoutInflater();
				linha = inflater.inflate(R.layout.linha, parent, false);
				armazenador = new ArmazenadorRestaurante(linha);
				linha.setTag(armazenador);
			} else {
				armazenador = (ArmazenadorRestaurante) linha.getTag();
			}

			armazenador.popularFormulario(listaRestaurantes.get(position));

			return linha;
		}
	}

	static class ArmazenadorRestaurante {
		private TextView nome = null;
		private TextView endereco = null;
		private ImageView icone = null;

		ArmazenadorRestaurante(View linha) {
			nome = (TextView) linha.findViewById(R.id.titulo);
			endereco = (TextView) linha.findViewById(R.id.endereco);
			icone = (ImageView) linha.findViewById(R.id.icone);
		}

		void popularFormulario(Restaurante r) {
			nome.setText(r.getNome());
			endereco.setText(r.getEndereco());

			if (r.getTipo().equals("rodizio")) {
				icone.setImageResource(R.drawable.rodizio);
			} else if (r.getTipo().equals("fast_food")) {
				icone.setImageResource(R.drawable.fast_food);
			} else {
				icone.setImageResource(R.drawable.entrega);
			}
		}
	}
}

Se você perdeu alguma coisa, ou quiser baixar o projeto, basta clicar aqui.

Até o próximo post!

Deixe uma resposta