Tempo de leitura: 4 minutos

UPDATE: hoje em dia temos excelentes bibliotecas para networking com Android, o Volley e o RetroFit são as mais usadas atualmente.

Você menino maroto e faceiro desenvolvedor mobile está lá desenvolvendo seu app usando um virtual device com android 2.1 e tudo funciona maravilhosamente, você se acha o cara, já pensa em dominar o mundo desenvolvendo um aplicativo de alto nível que será vendido por um preço baixo, mas, que terá milhões de downloads e lhe renderá uma boa grana e visibilidade, fazendo o seu projeto ser comprado por um gigante como google ou facebook, quando de repente você vai testar seu app em um virtual device com android honeycomb ou superior e então…

log_networkonmainthread

Por que ocorre essa exceção?

Essa exceção ocorre a partir da API 11 ou versão 3.0 do android, como o próprio nome já diz, o motivo da exceção ocorrer é a aplicação estar efetuando algum processo de network na thread principal (daí o nome MainThread), nas versões anteriores essa operação é permitida apesar de não ser recomendada, pois dependendo do tempo que o processo demora, pode ocorrer um ANR (Application Not Responding).

O ANR geralmente ocorre quando a aplicação não responde aos comandos do usuário ou um BroadcastReceiver  demora mais que 10 segundos, portanto ao fazer uma operação demorada na thread principal da aplicação que é a mesma thread da UI (User Interface ou simplesmente tela) sua aplicação pode travar, a partir da versão 3.0 do android foi implementada essa “proteção” no sistema, justamente para forçar o desenvolvedor a tomar um atitude quanto a essa questão, no Android a responsividade da aplicação é monitorada pelo Activity Manager e o Window Manager System Services.

Eu conheço 3 soluções para esse problema, uma ruim, uma razoável e uma ideal, abaixo vou descrevê-las.

Solução Ruim

A solução ruim é chamada dessa forma por não ser a solução recomendada para tratar o caso, geralmente é usada como paliativo para poder continuar o desenvolvimento da aplicação, porém, o correto é ser trocada pela solução ideal o quanto antes, para aplicar essa solução devemos usar o StrictMode para permitir que seja possível efetuar uma operação de rede na thread principal, o trecho de código abaixo faz a maldade.

[code lang=”java”]

package br.com.vandersonguidi.aplicacaoteste;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
public EditText etResultado;
public Button btnProcessar;
public TextView tvStatus;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);

setContentView(R.layout.activity_main);
btnProcessar = (Button) findViewById(R.id.btnProcessar);
etResultado = (EditText) findViewById(R.id.etResultado);
tvStatus = (TextView) findViewById(R.id.tvStatus);

btnProcessar.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
String URL = "http://vandersonguidi.com.br/networkonmainthread.txt";

try {

HttpClient client = new DefaultHttpClient();
HttpGet requisicao = new HttpGet();
requisicao.setHeader("Content-Type",
"text/plain; charset=utf-8");
requisicao.setURI(new URI(URL));
HttpResponse resposta = client.execute(requisicao);
BufferedReader br = new BufferedReader(
new InputStreamReader(resposta.getEntity()
.getContent()));
StringBuffer sb = new StringBuffer("");
String linha = "";

while ((linha = br.readLine()) != null) {
sb.append(linha);
}

br.close();

linha = sb.toString();

Toast.makeText(getApplicationContext(), linha,
Toast.LENGTH_LONG).show();
return;

} catch (Exception ex) {
tvStatus.setText(ex.getMessage());
}

}
});
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}

[/code]

Na prática estamos alterando a política de threads para a aplicação, porém, conforme já dito, essa não é a solução ideal, evite ao máximo utiliza-la.

Solução razoável

A solução razoável é chamada dessa forma pois apesar de ser um solução melhor do que a solução ruim, não é a solução recomendada na documentação da SDK do android.

o trecho de código abaixo faz a maldade

[code lang=”java”]
new Thread(new Runnable()

{
public void run() {
//faça qualquer coisa aqui
}
}).start();

[/code]

Na prática estamos criando uma nova instância de uma thread e  dentro dessa thread instanciamos um Runnable para efetuar uma operação, do lado de fora da thread chamamos o método start para executar a nossa operação.

Solução ideal

A solução ideal é chamada desta forma pois é a forma indicada pela comunidade android, para implementar esta solução devemos implementar uma herança da classe AsyncTask, essa classe possibilita efetuar operações no background
da aplicação sem a necessidade de manipular Threads ou Handlers e obviamente sem sacrificar a thread principal da aplicação.

A classe AsyncTask tem 3 parâmetros genéricos:

o primeiro parâmetro é um array de parâmetros que você deseja passar para a aplicação (geralmente alguma informação que você necessite para executar a ação, ex: para consultar um URL você pode passar uma lista de URL’s ou de Strings).

o segundo parâmetro é uma unidade de progresso, caso você queira enviar uma resposta para sua aplicação (ex: efetuando o download de um arquivo, é possivel informar para a aplicação o status do download ou atualizar um ProgressDialog).

O terceiro parâmetro é o tipo que a classe vai retornar, ex: uma string, que pode conter processo executado com sucesso ou processo executado com erro, ou uma classe que vai ser passada vazia pra aplicação e vai ser populada dentro do processo.

então supondo que eu vá criar uma classe que vá efetuar uma consulta em uma url e retornar para a minha aplicação o resultado dessa consulta, vou chamar essa classe simplesmente de Consulta, a declaração ficaria assim:

[code lang=”java”]
package br.com.vandersonguidi.aplicacaoteste;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.AsyncTask;

public class Consulta extends AsyncTask<String, Void, Boolean> {

@Override
protected Boolean doInBackground(String… params) {

String URL = "http://vandersonguidi.com.br/networkonmainthread.txt";
String linha = "";
Boolean Erro = true;

if (params.length > 0)
// faço qualquer coisa com os parâmetros

try {

HttpClient client = new DefaultHttpClient();
HttpGet requisicao = new HttpGet();
requisicao.setHeader("Content-Type",
"text/plain; charset=utf-8");
requisicao.setURI(new URI(URL));
HttpResponse resposta = client.execute(requisicao);
BufferedReader br = new BufferedReader(new InputStreamReader(
resposta.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");

while ((linha = br.readLine()) != null) {
sb.append(linha);
}

br.close();

linha = sb.toString();
Erro = false;

} catch (Exception e) {
Erro = true;
}

return Erro;
}
}

[/code]

E para chamar o método eu simplesmente uso o seguinte código:

[code lang=”java”]
new Consulta().execute(seuparametro);
[/code]

Lembrando que esse código simplesmente vai executar o processo de background, caso você queira receber o resultado, que no nosso caso é um booleano indicando se ocorreu erro ou não, você pode usar a seguinte linha de código.

[code lang=”java”]
try {
Boolean Resultado = new Consulta().execute(seuparametro).get();
} catch (Exception ex) {
tvStatus.setText(ex.getMessage());
}
[/code]

Bom, então é isso, não esqueçam que para efetuar qualquer requisição na internet é preciso adicionar a permissão

[code language=”xml”]<uses-permission android:name="android.permission.INTERNET"></uses-permission>[/code]

no arquivo manifest, qualquer dúvida basta perguntar que eu respondo assim que possível.

Este post te ajudou? caso sim, clica no joinha aí embaixo e avalia com 10 estrelas, valeu =)

VN:F [1.9.22_1171]
Rating: 9.5/10 (73 votes cast)
VN:F [1.9.22_1171]
Rating: +45 (from 45 votes)
Resolvendo o erro NetworkOnMainThreadException no desenvolvimento Android, 9.5 out of 10 based on 73 ratings