Quem de nós nunca se deparou com o seguinte diálogo ao executar uma aplicação Android?
Mas o que causa esse problema?
Este é um diálogo mostrado pelo sistema quando ao identificar que a aplicação está executando muitas operações na main thread – a thread principal ou UI thread.
A UI thread é responsável pelas interações com o usuário. Por isso, quando o sistema percebe que ela está fazendo muito processamento, ele considera que a aplicação não está respondendo ao usuário e, portanto, está travada. Neste momento ele dá a opção de fechar a aplicação – o inconveniente diálogo application not responding – ou simplesmente ANR.
Como evitar um ANR
Mas como evitar um ANR? Simples! Basta executar as operações demoradas (como acesso a rede, acesso a banco de dados, entre outros) de sua aplicação em uma thread separada. E para fazer isso existem três mecanismos:
- Threads (Java nativo)
- Asynctasks
- Handlers
O primeiro mecanismo (threads nativas de java) funciona, porém não é recomendado em Android – principalmente porque as outras implementações permitem que uma parte da execução seja feita na UI thread, o que geralmente é necessário. Os Handlers, por sua vez, são ferramentas poderosas e serão tratados em um próximo capÃtulo. Neste capÃtulo estudaremos as AsyncTasks.
AsyncTasks
As AsyncTasks são classes do framework capazes de executar código em uma thread separada, quando este é colocado dentro de seu método doInBackground.
Para implementar uma AsyncTask, basta criar uma subclasse desta e fazer override dos métodos necessários. Os principais métodos desta classe são:
- doInBackground: como já dito, esse é o método responsável por executar código em segundo plano (em uma thread separada)
- onPreExecute: este método é executado assim que a AsyncTask inicia sua execução, antes do método doInBackground. Ele é executado na UI thread.
- onPostExecute: assim como o onPreExecute, este método é executado na UI thread, porém depois do método doInBackground.
- onProgressUpdate: este método é invocado quando é alterado o progresso. (O progresso pode ser alterado a qualquer momento durante a execução de doInBackground, através do método setProgress) Este mecanismo é geralmente usado para a criação de barras de progresso.
No nosso projeto QuickNotes, utilizado neste curso, temos um problema evidente na MainActivity: o método insertNote acessa o banco de dados (uma operação que pode ser demorada). Iremos resolver este problema implementando uma AsyncTask, da seguinte forma:
private class AddNoteTask extends AsyncTask<String, Void, Uri> { @Override protected Uri doInBackground(String... string) { ContentValues values = new ContentValues(); values.put(QuickNotesProvider.Notes.TEXT, string[0]); return getContentResolver().insert( QuickNotesProvider.Notes.CONTENT_URI, values); } @Override protected void onPostExecute(Uri result) { // Após a inserção da nota, vamos mostrar um Toast ao usuário Toast toast = Toast.makeText(MainActivity.this, "Nota inserida com sucesso!", Toast.LENGTH_SHORT); toast.show(); } }
Executando a AsyncTask
Com a AsyncTask criada basta executá-la no método addNote, que agora irá lançar a AsyncTask ao invés de inserir diretamente no banco de dados.
/* * Método responsável por inserir um registro no content provider */ protected void addNote(String text) { AddNoteTask task = new AddNoteTask(); // É necessário passar como parâmetro um array de strings, // que será o parâmetro recebido pelo método doInBackground. // Neste caso, será passado um array com apenas um item: A nova nota task.execute(new String[] { text } ); }
Por fim, a MainActivity fica assim:
package br.com.felipesilveira.quicknotes; import android.app.ListActivity; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter; import android.widget.Toast; public class MainActivity extends ListActivity { private static final String TAG = "QuickNotesMainActivity"; private Cursor mCursor; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "Criando a MainActivity"); setContentView(R.layout.main); Intent i = new Intent(this, WelcomeActivity.class); startActivity(i); Button insertButton = (Button)findViewById(R.id.insert_button); insertButton.setOnClickListener(mInsertListener); // adicionando um 'Hint' ao Editbox. EditText editBox = (EditText)findViewById(R.id.edit_box); editBox.setHint("Nova nota..."); mCursor = this.getContentResolver(). query(QuickNotesProvider.Notes.CONTENT_URI, null, null, null, null); ListAdapter adapter = new SimpleCursorAdapter( // O primeiro parametro é o context. this, // O segundo, o layout de cada item. R.layout.list_item, // O terceiro parametro eh o cursor que contem os dados // a serem mostrados mCursor, // o quarto parametro eh um array com as colunas do cursor // que serao mostradas new String[] {QuickNotesProvider.Notes.TEXT}, // o quinto parametro é um array (com o mesmo tamanho // do anterior) com os elementos que receberao // os dados. new int[] {R.id.text}); setListAdapter(adapter); } /* * Definindo um OnClickListener para o botão "Inserir" */ private OnClickListener mInsertListener = new OnClickListener() { public void onClick(View v) { EditText editBox = (EditText)findViewById(R.id.edit_box); addNote(editBox.getText().toString()); editBox.setText(""); } }; /* * Método responsável por inserir um registro no content provider */ protected void addNote(String text) { AddNoteTask task = new AddNoteTask(); // É necessário passar como parâmetro um array de strings, // que será o parâmetro recebido pelo método doInBackground. // Neste caso, será passado um array com apenas um item: A nova nota task.execute(new String[] { text } ); } private class AddNoteTask extends AsyncTask<String, Void, Uri> { @Override protected Uri doInBackground(String... string) { ContentValues values = new ContentValues(); values.put(QuickNotesProvider.Notes.TEXT, string[0]); return getContentResolver().insert( QuickNotesProvider.Notes.CONTENT_URI, values); } @Override protected void onPostExecute(Uri result) { // Após a inserção da nota, vamos mostrar um Toast ao usuário Toast toast = Toast.makeText(MainActivity.this, "Nota inserida com sucesso!", Toast.LENGTH_SHORT); toast.show(); } } }
Para fazer download do projeto inteiro, clique aqui.

[…] ainda que a rede ou o dispositivo seja lento. Lembre-se de fazer todas as tarefas complexas em threads em background. Dessa forma, você evita os temidos “ANRs” (Application not responding) e cria uma […]