AsyncTask: Trabalhando com tarefas assíncronas

Veja neste artigo como trabalhar com tarefas assíncronas no desenvolvimento de aplicações Android nativas utilizando a classe AsyncTask para facilitar a comunicação de uma thread qualquer com a UI thread.

Um dos grandes desafios dos programadores individuais e mesmo de empresas que produzem aplicações nativas da plataforma Android, é a administração da concorrência dentro destas aplicações. Os usuários estão cansados de reclamar de aplicações que são lentas, que consomem muita memória, ou mesmo que travam durante a execução de alguma tarefa relevante para o bom funcionamento da aplicação.

É muito importante ressaltar que um aplicativo desenvolvido com problemas de concorrência, ou mesmo com um mal gerenciamento da mesma, é um investimento ruim, pois se ele é avaliado negativamente no Google Play, ou se surgem comentários ruins sobre ele em outro lugar da internet assim que ele é lançado, mesmo ocorrendo novas atualizações, as quais sanem os problemas de concorrência, muitos potenciais usuários deixaram de baixá-lo. Em muitos casos, podemos dizer que o aplicativo morreu assim que nasceu.

Trabalhar na administração, criação e no gerenciamento de novas threads no desenvolvimento de aplicações Android não é uma tarefa trivial, no entanto a Classe AsyncTask permite que você consiga gerenciar esse processamento de forma que a complexidade do gerenciamento “manual“ não seja exposta ao programador. Vale lembrar que o programador de aplicações Android necessita obrigatoriamente conhecer o funcionamento do gerenciamento de threads.

Através de uma aplicação relativamente simples, você aprenderá como utilizar a classe AsyncTask para fazer a comunicação de uma thread qualquer com a UI thread.

Você deve estar se perguntando como isso é útil de forma prática? A UI thread é a única thread que pode modificar a interface gráfica e a principal thread de sua aplicação. A grande questão é, se você colocar todo o fluxo de processamento na thread principal, em algum momento a sua aplicação vai se tornar lenta ou travar. É claro que na prática ninguém faz isso. O exemplo demonstrará como realizar um processamento mais oneroso (no caso um contador) para a aplicação em uma thread. O resultado desse processamento será exibido na tela a cada ciclo, através da transferência de responsabilidade da thread que fez esse processamento, para a UI thread, a qual é a única thread que pode atualizar a interface gráfica.

Primeiro vamos criar um projeto com as configurações presentes nas Figuras 1 a 5.

Nota: No passo 2 os campos foram deixados em branco para sinalizar que você pode definir as nomenclaturas que quiser. No entanto, as que foram usadas são as seguintes: nome da aplicação=Ay, nome do projeto=Ay, nome do pacote=com.as.
Figura 1. Passo 1 - Novo projeto
Figura 2. Passo 2 - Nova aplicação android
Figura 3. Passo 3 – Criando projeto
Figura 4. Passo 4 – Criando atividade
Figura 5. Passo 5 – Definindo atividades

Com o projeto criado, vamos fazer algumas alterações para o devido funcionamento da nossa aplicação, são elas:

Primeiro vamos alterar a codificação do arquivo strings.xml para ele ficar igual ao seguinte. Veja a Listagem 1.

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Ay</string> <string name="action_settings">Settings</string> <string name="start">Processar..</string> </resources>
Listagem 1. Exemplo de como o arquivo strings.xml ficará

A listagem acima “não tem tanta relevância pro funcionamento do aplicativo”. O arquivo strings.xml serve para mapear os elementos da tela e facilitar sua manutenção. Eles são os chamados resources.

Um exemplo é a tag string com o name=”start”, está mapeada para o elemento button na tela e está mapeada para o texto do botão.

Depois que o arquivo activity_as.xml for alterado ele vai ficar como a Listagem 2.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/LinearLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".AsActivity" > <Button android:id="@+id/bt" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/start" android:onClick="processamento" android:layout_gravity="center" /> <TextView android:id="@+id/tex" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="25pt" android:text android:textColor="#00FF00" /> </LinearLayout>
Listagem 2. Exemplo de como o arquivo activity_as.xml vai ficar

A listagem acima cuida da disposição dos elementos na tela, bem como o layout e os elementos que vão dentro do layout, que no caso são um botão e um textview. O botão referência a tag string com o name start através do item android:text.

Existe também um item bastante importante no elemento button para o qual precisamos atentar: o android:onclick. Este item diz que quando o botão for clicado, um método com o valor dele será invocado. Também não podemos nos esquecer do item adroid:id, o qual será a nossa porta de entrada para o acesso a esses elementos mediante outras classes.

O aquivo AsActivity deve estar inicialmente como referenciado na Listagem 3.

package com.as; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class AsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_as); } @Override public boolean onCreateOptionsMenu(Menu menu) { //Inflate the menu; this adds items to the action //bar if it is present. getMenuInflater().inflate(R.menu.as, menu); return true; } }
Listagem 3. Exemplo de como o arquivo deve estar inicialmente

Após sua alteração, a classe deve estar como a Listagem 4.

package com.as; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.widget.Button; import android.widget.TextView; public class AsActivity extends Activity { private TextView tex; private Button bt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_as); //Recupera o textView da activity //activity_as referenciada acima //pelo setContentView. tex = (TextView) findViewById(R.id.tex); //Recupera o Button view da activity //activity_as referenciada acima //pelo setContentView. bt = (Button) findViewById(R.id.bt); } public void processamento(View view){ } }
Listagem 4. Exemplo de como a classe vai ficar

No caso acima, o processo acontece da seguinte forma: quando a activity é iniciada, o método onCreate é chamado, esse por sua vez carrega a tela através do método setContentView, o qual recebe uma referência ao arquivo activity_as.xml (no caso é o arquivo que responsável pelos elementos da tela). Depois o botão e a textview são recuperadas através de uma chamada a findViewById para que esses itens possam ser manipulados. O nosso objetivo é usar essas referências no método processamento, o qual será invocado quando o botão for clicado, mas como este método ainda está vazio, falaremos dele mais a frente.

Agora só falta criar a classe Num, ela vai ser criada no pacote onde foi criada a classe que comanda a activity principal. Observe a Listagem 5.

package com.as; import android.os.AsyncTask; import android.widget.Button; import android.widget.TextView; public class Num extends AsyncTask<Integer, Integer, Void>{ private TextView text; private Button bt; public Num(TextView text, Button bt) { this.text = text; this.bt = bt; } //É onde acontece o processamento //Este método é executado em uma thread a parte, //ou seja ele não pode atualizar a interface gráfica, //por isso ele chama o método onProgressUpdate, // o qual é executado pela //UI thread. @Override protected void doInBackground(Integer... params) { int n = params[0]; int i = 0; while(i < n){ try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } //Notifica o Android de que ele precisa //fazer atualizações na //tela e chama o método onProgressUpdate //para fazer a atualização da interface gráfica //passando o valor do //contador como parâmetro. publishProgress(i); i++; } return null; } // É invocado para fazer uma atualização na // interface gráfica @Override protected void onProgressUpdate(Integer... values) { text.setText(String.valueOf(values[0])); } }
Listagem 5. Exemplo de como a classe vai ficar

A classe AsyncTask tem como sua função prioritária executar código que demanda mais poder de processamento em um thread diferente da UI thread, e o melhor disso é que ele não põe você em contato direto com a complexidade da programação tradicional para a manipulação de threads. No caso acima, o comentário do código já adianta grande parte do trabalho, no entanto, é preciso dizer que pra utilizar a classe Asynctask você precisa estender ela passando alguns parâmetros na forma de generics. Generics é um conceito não particular da programação android e serve para parametrizar os tipos de dados passados. É recomendado que você já conhecesse o funcionamento deles, pois eles são bastante importantes em algumas situações da programação android.

Já os métodos funcionam da seguinte forma: o método doInBackground executa fora da UI thread e simula o processamento que queremos fazer através de um loop, o qual cria um contador. A cada ciclo de processamento desse contador, o método publishProgress(i) é invocado e o valor de i é passado. A função desse método é invocar o método de baixo, no caso o onProgressUpdate, o qual executa da UI thread e faz a atualização da interface gráfica.

Precisamos ainda acrescentar algumas linhas de código à classe que comanda a activity principal para que o projeto execute com sucesso. Veja a Listagem 6.

package com.as; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class AsActivity extends Activity { private TextView text; private Button bt; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_as); text = (TextView) findViewById(R.id.texto); bt = (Button) findViewById(R.id.botao); } public void processamento(View view){ Num num = new Num(text, bt); // Executa o doInBackground e passa o valor 50 como parâmetro num.execute(50); } }
Listagem 6. Exemplo de como a classe vai ficar

O que vamos fizemos foi apenas colocar o código necessário para que possamos utilizar a classe Num a partir do click do botão. Neste caso vamos instancia-la e depois usar o método, passando o valor que queremos como parâmetro para a contagem dos ciclos.

Depois de terminado, você pode executar o projeto no emulador android e ver como a aplicação se comporta.

Bibliografia:

Confira também

Artigos relacionados