Trabalhando com AsyncTask no Android

A classe AsyncTask foi criada para facilitar o processamento em background e atualizar os dados na Interface gráfica. Veja nesse artigo com funciona esse processo.

Programar em Android não é uma tarefa impossível, bastando ter conhecimentos em Java, pois a Google fornece uma API de acesso bem completa e documentada. Nesses dispositivos as tarefas podem ser executadas simultaneamente de duas formas: pelo “Paralelismo” em si e pelo PseudoParalelismo. O Paralelismo depende da quantidade de núcleos que o processador tem, ou seja, se ele tem dois núcleos, então apenas duas tarefas podem ser executadas simultaneamente. Agora, se ele tem apenas um núcleo, então ele executa apenas uma tarefa de cada vez, mas isso é inviável, pois não seria possível ouvir música, abrir o navegador e escrever um texto ao mesmo tempo.

Para esse problema ser solucionado nasceu o PseudoParalelismo, que nada mais é que um sistema interno que permite executar as tarefas um pouco por vez em um determinado tempo, ou seja, o escalonador de processos coloca a tarefa em execução por um tempo determinado e, acabando esse tempo, ocorre o “time-slice”, que tira o processo que está em execução e o coloca no final da fila dos processos que estão prontos para executar. Esse processo pode ir também para o estado bloqueado, significando que aguarda algum evento externo. Esse time-slice acontece em um tempo totalmente imperceptível, o que dá a sensação de paralelismo, mesmo quando existe apenas um único núcleo de processamento.

Dentro da plataforma Android pode-se executar tarefas em paralelo e para isso são utilizadas as Threads, que permitem a criação de linhas de execução a serem processadas em “paralelo". Isso é algo extremamente aconselhável que se faça no Android, já que esse acontece dentro da Activity principal.

O uso dessa prática é bem simples, mas para atualizar a interface gráfica com as informações processadas existe um trabalho um pouco maior, como a utilização de Handlers. A classe AsyncTask foi criada para facilitar esse tipo de processamento em background e atualizar os dados na Interface.

AsyncTask

A AsyncTask encapsula todo esse processo de criação de Threads e Handler. Para utilizá-la é bem simples, já que uma classe deve ser criada e estendida à classe AsyncTask. E por ser genérica, ela é definida da seguinte forma:

AsyncTask<Parâmetros,Progresso,Resultado>

Existem quatro métodos que podem ser sobrescritos na AsyncTask:

A Listagem 1 mostra a estrutura de uma classe que estende de AsyncTask.

public class SuaClasse extends AsyncTask<String,Integer,Integer>{ @Override protected void onPreExecute(){ //Codigo } @Override protected Integer doInBackground(String... params) { //Codigo } @Override protected void onPostExecute(Integer numero){ //Codigo } protected void onProgressUpdate(Integer… params){ //Codigo } }
Listagem 4. Exemplo de como a classe vai ficar

Agora será mostrado um exemplo prático onde a aplicação irá receber uma URL contendo apenas uma imagem que será baixada e mostrada na tela do usuário. Todo processamento será feito em uma Thread separada.

O layout terá três elementos: um EditText que receberá a URL, um Button para acionar a ação de baixar a imagem e um ImageView responsável por mostrar o Bitmap na tela. A Listagem 2 mostra esse código.

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Baixar" android:id="@+id/button" android:layout_below="@+id/editText" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imageView" android:layout_below="@+id/button" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout>
Listagem 2. Código XML do layout

A Figura 1 mostra como esse layout deve parecer.

Figura 1. Layout

O arquivo de manifesto deve conter as informações a seguir, mostradas na Listagem 3. A permissão de acessar a internet deve ser dada, pois a imagem a ser baixada será tirada de lá.

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="https://schemas.android.com/apk/res/android" package="br.com.home.asynctask" > <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Listagem 3. Android Manifest

O código responsável por baixar a imagem não tem segredo nenhum e pode ser visto na Listagem 4. Esse código está contido dentro de uma classe Auxiliar.

package br.com.home.asynctask; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import java.io.IOException; import java.io.InputStream; import java.net.URL; /** * Created by allanromanato on 9/30/15. */ public class Auxiliar { public static Bitmap baixarImagem(String url) throws IOException{ URL endereco; InputStream inputStream; Bitmap imagem; endereco = new URL(url); inputStream = endereco.openStream(); imagem = BitmapFactory.decodeStream(inputStream); inputStream.close(); return imagem; } }
Listagem 1. NOME

Por conter um método estático, essa classe não precisa ser instanciada para acessar o método baixarImagem. Aqui é definida a URL que contém a imagem e seu endereço é passado por parâmetro. Um objeto do tipo InputStream é responsável por receber as informações da imagem, que por enquanto ainda está codificada. Por fim, o objeto do tipo Bitmap recebe o retorno do método estático decodeStream contido dentro da classe BitmapFactory, que é responsável por converter para Bitmap o que estava armazenado dentro do inputStream. Este último tem sua conexão encerrada e a imagem é retornada.

Veremos a seguir como montar o código principal da nossa aplicação começando pelo código da Listagem 5.

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imagem = (ImageView)findViewById(R.id.imageView); baixar = (Button)findViewById(R.id.button); endereco = (EditText)findViewById(R.id.editText); Log.i("AsyncTask", "Elementos de tela criados e atribuidos Thread: " + Thread.currentThread().getName()); baixar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("AsyncTask", "Botão Clicado Thread: " + Thread.currentThread().getName()); chamarAsyncTask(endereco.getText().toString()); } }); } private void chamarAsyncTask(String endereco){ TarefaDownload download = new TarefaDownload(); Log.i("AsyncTask", "AsyncTask senado chamado Thread: " + Thread.currentThread().getName()); download.execute(endereco); }
Listagem 5. Primeira parte do código

O básico de qualquer aplicação Android pode ser observado nesse código, onde os objetos do tipo ImageView, Button e EditText tem seus respectivos representantes no XML atrelado. No botão é ligado tem um onClickListner, que identificará quando ele for pressionado para executará o conteúdo do método onClick. Nesse caso tem-se a chamada ao método chamarAsyncTask, passando por parâmetro o conteúdo digitado dentro do EditText. Ao chamá-lo, este instancia um objeto do tipo TarefaDownload.

É muito importante entender que nunca se deve chamar os métodos da AsyncTask separadamente, pois eles não funcionarão: para o correto funcionamento deve-se utilizar o objeto da classe estendida à AsyncTask e chamar o método para que execute, passando os paramentos que são necessários, garantindo assim o correto funcionamento.

Notem também que cada parte do código tem um Log.i para observar no log do Android onde está sendo executada cada tarefa.

Na Listagem 6 pode-se observar a classe que estende de AsyncTask.

private class TarefaDownload extends AsyncTask<String, Void, Bitmap>{ @Override protected void onPreExecute(){ Log.i("AsyncTask", "Exibindo ProgressDialog na tela Thread: " + Thread.currentThread().getName()); load = ProgressDialog.show(MainActivity.this, "Por favor Aguarde ...", "Baixando Imagem ..."); } @Override protected Bitmap doInBackground(String... params) { Bitmap imagemBitmap = null; try{ Log.i("AsyncTask", "Baixando a imagem Thread: " + Thread.currentThread().getName()); imagemBitmap = Auxiliar.baixarImagem(params[0]); }catch (IOException e){ Log.i("AsyncTask", e.getMessage()); } return imagemBitmap; } @Override protected void onPostExecute(Bitmap bitmap){ if(bitmap!=null) { imagem.setImageBitmap(bitmap); Log.i("AsyncTask", "Exibindo Bitmap Thread: " + Thread.currentThread().getName()); }else{ Log.i("AsyncTask", "Erro ao baixar a imagem " + Thread.currentThread().getName()); } Log.i("AsyncTask", "Tirando ProgressDialog da tela Thread: " + Thread.currentThread().getName()); load.dismiss(); } }
Listagem 6. Classe privada responsável por executar o paralelismo

Dentro da classe TarefaDownload pode-se notar todos os métodos comentados anteriormente:

Notem que em todos os Logs tem uma chamada Thread.currentThread().getName(): isso é utilizado para mostrar no log em qual Thread cada parte do código está executando. Na Listagem 7 pode-se ver o resultado soltado no Log.

10-01 22:33:47.031 4598-4598/br.com.home.asynctask I/AsyncTask﹕ Elementos de tela criados e atribuidos Thread: main 10-01 22:34:36.865 4598-4598/br.com.home.asynctask I/AsyncTask﹕ Botão Clicado Thread: main 10-01 22:34:36.869 4598-4598/br.com.home.asynctask I/AsyncTask﹕ AsyncTask senado chamado Thread: main 10-01 22:34:36.869 4598-4598/br.com.home.asynctask I/AsyncTask﹕ Exibindo ProgressDialog na tela Thread: main 10-01 22:34:36.893 4598-4618/br.com.home.asynctask I/AsyncTask﹕ Baixando a imagem Thread: AsyncTask #1 10-01 22:34:38.470 4598-4598/br.com.home.asynctask I/AsyncTask﹕ Exibindo Bitmap Thread: main 10-01 22:34:38.470 4598-4598/br.com.home.asynctask I/AsyncTask﹕ Tirando ProgressDialog da tela Thread: main
Listagem 7. Logs das threads

Observem que a maioria dos logs mostra o comando que foi executado na Thread main, com exceção do "Baixando Imagem", que executa dentro da Thread AsyncTask #1, ou seja, ele está de fato sendo executado fora da Thread principal, em uma linha de execução diferente.

A seguir, as Figuras 2, 3 e 4 mostram o resultado final dessa operação.

Figura 2. Tela inicial
Figura 3. Aguardando
Figura 4. Resultado final

A Figura 2 mostra a tela antes de ser inserida a URL para capturar a imagem, enquanto a Figura 3 mostra a tela enquanto o usuário está aguardando a finalização do download da mesma. Por fim, a Figura 4 mostra o resultado final para o usuário.

Na Listagem 8 temos o código completo para quem perdeu alguns passos da criação da classe AsyncTask, responsável pelo PseudoParalelismo.

package br.com.home.asynctask; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import java.io.IOException; public class MainActivity extends Activity { private ImageView imagem; private Button baixar; private EditText endereco; private ProgressDialog load; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imagem = (ImageView)findViewById(R.id.imageView); baixar = (Button)findViewById(R.id.button); endereco = (EditText)findViewById(R.id.editText); Log.i("AsyncTask", "Elementos de tela criados e atribuidos Thread: " + Thread.currentThread().getName()); baixar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.i("AsyncTask", "Botão Clicado Thread: " + Thread.currentThread().getName()); chamarAsyncTask(endereco.getText().toString()); } }); } private void chamarAsyncTask(String endereco){ TarefaDownload download = new TarefaDownload(); Log.i("AsyncTask", "AsyncTask senado chamado Thread: " + Thread.currentThread().getName()); download.execute(endereco); } private class TarefaDownload extends 1AsyncTask<String, Void, Bitmap>{ @Override protected void onPreExecute(){ Log.i("AsyncTask", "Exibindo ProgressDialog na tela Thread: " + Thread.currentThread().getName()); load = ProgressDialog.show(MainActivity.this, "Por favor Aguarde ...", "Baixando Imagem ..."); } @Override protected Bitmap doInBackground(String... params) { Bitmap imagemBitmap = null; try{ Log.i("AsyncTask", "Baixando a imagem Thread: " + Thread.currentThread().getName()); imagemBitmap = Auxiliar.baixarImagem(params[0]); }catch (IOException e){ Log.i("AsyncTask", e.getMessage()); } return imagemBitmap; } @Override protected void onPostExecute(Bitmap bitmap){ if(bitmap!=null) { imagem.setImageBitmap(bitmap); Log.i("AsyncTask", "Exibindo Bitmap Thread: " + Thread.currentThread().getName()); }else{ Log.i("AsyncTask", "Erro ao baixar a imagem " + Thread.currentThread().getName()); } Log.i("AsyncTask", "Tirando ProgressDialog da tela Thread: " + Thread.currentThread().getName()); load.dismiss(); } } }
Listagem 8. Código principal

Como pode ser visto, a AsyncTask permite o gerenciamento e a criação de threads para a execução concorrente de aplicações sem muita complexidade que o assunto originalmente tem. Vale lembrar que é necessário pelo menos ter conhecimento prévio do funcionamento e gerenciamento de threads.

Confira também

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados