Tutorial Android #16 – Utilizando um IntentService

Padrão

Olá pessoal! No tutorial anterior, utilizamos uma AsyncTask para recuperar o conteúdo do Twitter. Isso foi necessário para que pudéssemos obter comunicação com a rede fora da thread principal do aplicativo e, portanto, evitar lentidão na interface. Outra forma de resolver esse problema é usando um IntentService. Um IntentService é um componente à parte que aceita comandos vindos de uma Activity, executa os comandos em linhas em background e, opcionalmente, responde às atividades ou o usuário. Neste tutorial, vamos configurar como um IntentService como um substituto para a AsyncTask.

Todos prontos?

Primeiramente, crie uma nova classe no pacote net.rafaeltoledo.restaurante chamada TwitterService, estendendo IntentService:

package net.rafaeltoledo.restaurante;

import android.app.IntentService;
import android.content.Intent;

public class TwitterService extends IntentService {

	public TwitterService() {
		super("TwitterService");
	}

	@Override
	protected void onHandleIntent(Intent intent) {

	}
}

Em seguida, vamos adicionar um novo nó service lá no arquivo AndroidManifest.xml logo após os nós activity, dentro do nó application.

<service android:name=".TwitterService">
</service>

O método onHandleIntent() da IntentService é chamado sempre em background, razão principal de a utilizarmos. Vamos começar com uma implementação inicial deste método lá na nossa classe TwitterService, importando parte da lógica que tínhamos no método doInBackground():

@Override
protected void onHandleIntent(Intent intent) {
	Twitter t = new TwitterFactory().getInstance();

	try {
		List<Status> resultado = t.getUserTimeline(intent.getStringExtra(PERFIL_EXTRA));
	} catch (Exception ex) {
		Log.e("ListaRestaurantes", "Erro manipulando timeline twitter", ex);
	}
}

Adicione também o atributo da classe referenciado no método, que servirá para obter o perfil do Twitter que obteremos os tweets.

public static final String PERFIL_EXTRA = "net.rafaeltoledo.PERFIL_EXTRA";

Continuando, precisamos agora enviar os tweets para a Activity. Para realizar a comunicação, utilizaremos um Messenger, que servirá para obtermos informações do serviço. Dessa forma, atualize a implementação do método onHandleIntent():

@Override
protected void onHandleIntent(Intent intent) {
	Twitter t = new TwitterFactory().getInstance();
	Messenger messenger = (Messenger) intent.getExtras().get(MESSENGER_EXTRA);
	Message msg = Message.obtain();

	try {
		List<Status> resultado = t.getUserTimeline(intent.getStringExtra(PERFIL_EXTRA));

		msg.arg1 = Activity.RESULT_OK;
		msg.obj = resultado;
	} catch (Exception ex) {
		Log.e("ListaRestaurantes", "Erro manipulando timeline twitter", ex);
		msg.arg1 = Activity.RESULT_CANCELED;
		msg.obj = ex;
	}

	try {
		messenger.send(msg);
	} catch (Exception ex) {
		Log.w("ListaRestaurantes", "Erro enviando dados para a Activity", ex);
	}
}

Para completar, só precisamos adicionar o atributo MESSENGER_EXTRA a nossa classe.

public static final String MESSENGER_EXTRA = "net.rafaeltoledo.MESSENGER_EXTRA";

Por fim, vamos fazer as modificações na TwitterActivity para que ela trabalhe com o TwitterService em vez da TarefaTwitter.

Primeiramente, vamos converter a nossa TarefaTwitter para HandlerTwitter, que estenderá Handler em vez de AsyncTask. Os métodos anexar() e desanexar() serão mantidos para gerenciar as mudanças na configuração. Já o método doInBackground() será removido, já que a lógica foi movida para o serviço. O método onPostExecute() vira handleMessage(), para pegar o objeto Message do TwitterService, chamando os métodos atribuirTweets() ou atirarErro() dependendo do retorno do serviço. O resultado será esse:

private static class HandlerTwitter extends Handler {
	private TwitterActivity activity = null;

	HandlerTwitter(TwitterActivity activity) {
		anexar(activity);
	}

	void anexar(TwitterActivity activity) {
		this.activity = activity;
	}

	void desanexar() {
		activity = null;
	}

	@Override
	public void handleMessage(Message msg) {
		if (msg.arg1 == RESULT_OK) {
			activity.atribuirTweets((List<Status>) msg.obj);
		} else {
			activity.atirarErro((Exception) msg.obj);
		}
	}
}

Como não temos mais a TarefaTwitter, não precisamos mais dele no StatusInstancia. Porém, precisamos guardar nosso Handler como parte de nosso status, de forma que quando o usuário rotacionar a tela, nosso  objeto Messenger ainda possa comunicar-se corretamente com a TwitterActivity. Assim, modifique a classe StatusInstancia:

private static class StatusInstancia {
	List<Status> tweets = null;
	HandlerTwitter handler = null;
}

Assim, também precisaremos modificar o método onRetainNonConfigurationInstance() para acomodar o Handler em vez da tarefa.

@Override
public Object onRetainNonConfigurationInstance() {
	if (status.handler != null) {
		status.handler.desanexar();
	}
	return status;
}

Por fim, vamos modificar o método onCreate() para trabalhar com o TwitterService, criando o Messenger caso o status seja nulo, ou anexando-o caso já exista:

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	status = (StatusInstancia) getLastNonConfigurationInstance();

	if (status == null) {
		status = new StatusInstancia();
		status.handler = new HandlerTwitter(this);

		Intent i = new Intent(this, TwitterService.class);
		i.putExtra(TwitterService.PERFIL_EXTRA, getIntent().getStringExtra(PERFIL));
		i.putExtra(TwitterService.MESSENGER_EXTRA, new Messenger(status.handler));

		startService(i);
	} else {
		if (status.handler != null) {
			status.handler.anexar(this);
		}

		if (status.tweets != null) {
			atribuirTweets(status.tweets);
		}
	}
}

Pronto! Já podemos executar novamente o aplicativo, e nenhuma mudança deve ser percebida.

Se nenhuma mudança é percebida, por que tudo isso? Bom, pode ser que neste caso a diferença não seja visível, mas imagine que em vez dos tweets fôssemos baixar um vídeo. Utilizando o IntentService, a operação ocorre sem estar vinculada a nenhuma Activity. Ou seja, o usuário não precisa ficar esperando o download terminar para continuar. O IntentService fará o download por si próprio e se auto-destruirá quando terminar. 🙂

O projeto pode ser baixado aqui. E aguardem o próximo post, pois vai ser bacanudo! 😀

Até logo!

3 comentários sobre “Tutorial Android #16 – Utilizando um IntentService

    • O BroadcastReceiver é um mecanismo que você utiliza para que determinada funcionalidade seja iniciada quando o sistema é inicializado. Já um IntentService, é mais ou menos como um Intent mesmo, mas é como um “serviço” que roda o tempo todo na sua aplicação, aguardando comandos ou fornecendo dados.

  1. Felipe Sá

    Olá Rafael, quando clico no Timeline Twitter da minha aplicação, ele gerar uma exceção. O que pode ser? Obrigado. E seu blog é ótimo, está me ajudando demais.

Deixe uma resposta