Como corrigir 'android.os.NetworkOnMainThreadException'?

2394

Ocorreu um erro ao executar meu projeto Android para RssReader.

Código:

URL url = new URL(urlToRssFeed);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLReader xmlreader = parser.getXMLReader();
RssHandler theRSSHandler = new RssHandler();
xmlreader.setContentHandler(theRSSHandler);
InputSource is = new InputSource(url.openStream());
xmlreader.parse(is);
return theRSSHandler.getFeed();

E mostra o erro abaixo:

android.os.NetworkOnMainThreadException

Como posso corrigir esse problema?

bejoy george
fonte
131
Leia esta postagem no blog NetworkOnMainThreadException para obter mais informações. Explica por que isso ocorre no Android 3.0 e superior.
Adrian Monk
6
Para estar no caminho certo, leia primeiro os Pedidos de Rede no Android, então eu recomendaria estudar "Voleibol".
Anuj Sharma
3
Existem muitas bibliotecas alternativas que resolvem esse problema. Muitos estão listados na parte inferior desta página . Se você tem mais, nós levá-los :)
Snicolas
Você precisa executar atividades da Internet em um thread separado do thread principal (UI)
Naveed Ahmad 30/14
"Devido a um erro nas versões anteriores do Android, o sistema não sinalizou a gravação em um soquete TCP no segmento principal como uma violação de modo estrito. O Android 7.0 corrige esse erro. Os aplicativos que exibem esse comportamento agora lançam um android.os. NetworkOnMainThreadException. " - Então, alguns de nós não atingimos isso até recentemente! Veja também
Jay Jay

Respostas:

2547

Essa exceção é lançada quando um aplicativo tenta executar uma operação de rede em seu encadeamento principal. Execute seu código em AsyncTask:

class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {

    private Exception exception;

    protected RSSFeed doInBackground(String... urls) {
        try {
            URL url = new URL(urls[0]);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader xmlreader = parser.getXMLReader();
            RssHandler theRSSHandler = new RssHandler();
            xmlreader.setContentHandler(theRSSHandler);
            InputSource is = new InputSource(url.openStream());
            xmlreader.parse(is);

            return theRSSHandler.getFeed();
        } catch (Exception e) {
            this.exception = e;

            return null;
        } finally {
            is.close();
        }
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

Como executar a tarefa:

No MainActivity.javaarquivo você pode adicionar esta linha dentro do seu oncreate()método

new RetrieveFeedTask().execute(urlToRssFeed);

Não se esqueça de adicionar isso ao AndroidManifest.xmlarquivo:

<uses-permission android:name="android.permission.INTERNET"/>
Michael Spector
fonte
37
Eu acho que vale a pena notar aqui que o trecho de código acima deve ser uma subclasse (classe interna), de preferência privada. Dessa forma, quando o AsyncTask terminar, você ainda poderá manipular as entranhas da sua classe.
Dislexicanaboko
4
Na verdade eu fiz a mesma coisa que u mencionado acima, mas m enfrentando esse erro java.lang.RuntimeException: Não é possível criar manipulador dentro de segmento que não chamou Looper.prepare ()
Dhruv Tyagi
68
Esta é exatamente a resposta errada. Eu me deparei com isso o tempo todo no código das pessoas, e é irritante ter que corrigi-lo o tempo todo. O AsyncTask não deve ser usado para atividade de rede, porque está vinculado à atividade, mas não ao ciclo de vida da atividade. A rotação do dispositivo com esta tarefa está em execução causará uma exceção e trava o aplicativo. Use um IntentService que descarta dados no banco de dados sqlite.
Brill Pappin
5
Cuidado, o AsyncTask é frequentemente usado para operações de rede por atividade, quando não deveria. seu ciclo de vida não está sincronizado com a atividade. Para buscar dados, você deve usar um IntentService e o banco de dados atrás da exibição.
Brill Pappin
1
@BrillPappin, FWIW, este Guia do desenvolvedor do Android usa AsyncTaske cuida de uma alteração na configuração.
heyjude
677

Você quase sempre deve executar operações de rede em um thread ou como uma tarefa assíncrona.

Mas é possível remover essa restrição e você substitui o comportamento padrão, se estiver disposto a aceitar as consequências.

Adicionar:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy); 

Em sua classe,

e

ADICIONE essa permissão no arquivo manifest.xml do Android:    

<uses-permission android:name="android.permission.INTERNET"/>

Consequências:

Seu aplicativo (em áreas de conexão irregular à Internet) fica sem resposta e trava, o usuário percebe lentidão e precisa fazer uma matança forçada, e você corre o risco de o gerente de atividades matar o aplicativo e dizer ao usuário que o aplicativo parou.

O Android tem algumas boas dicas sobre boas práticas de programação para projetar a capacidade de resposta: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

user1169115
fonte
456
Esta é uma péssima ideia. a solução é evitar E / S de rede no thread principal (como mostra a resposta aceita).
MByD 14/05
74
Com isso, você apenas oculta seu problema real.
18712 Alex
28
@TwistedUmbrella O AsyncTask não adiciona uma página de código, adiciona 6 linhas (declaração de classe, anotação de substituição, doInBackgrounddeclaração, 2 colchetes e uma chamada para execute()). Por outro lado, mesmo uma única busca de um site, como você menciona, traz um atraso significativo na capacidade de resposta da interface do usuário. Não seja preguiçoso.
Zoltán
19
Eu acho que essa é a solução perfeita se você deseja executar apenas um pedaço de código de amostra para ver se algo funciona antes de implementar o AsyncTask adequado. É por isso que eu votei esta resposta, embora, como todos os outros disseram, isso não deva ser feito em um aplicativo de produção, apenas como uma solução rápida para um teste de desenvolvimento.
hooked82
94
Votado. Esta resposta está correta e, para muitos programadores que não são ingênuos nem estúpidos, mas que simplesmente exigem uma chamada SINCRONOSA (ou seja: isso deve bloquear o aplicativo ), é exatamente isso que é necessário. Estou mais do que feliz que o Android lança a exceção por padrão (IMHO, é uma coisa muito "útil" de fazer!) - mas também fico feliz em dizer "obrigado, mas - isso é realmente o que eu pretendia" e substituir isto.
Adam
428

Resolvi esse problema usando um novo Thread.

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 
Dr.Luiji
fonte
7
Em vez de criar um novo encadeamento sempre que desejar executar uma operação de rede, você também pode usar um único serviço executor de encadeamento.
Alex Lockwood
66
Simples, mas perigoso. O Runnable anônimo tem uma referência implícita à classe envolvente (por exemplo, sua Atividade ou Fragmento), impedindo que seja coletada como lixo até que o encadeamento seja concluído. Você deve pelo menos definir a prioridade como Process.BACKGROUND, caso contrário, esse encadeamento será executado com a mesma prioridade que o encadeamento principal / interface do usuário, contendendo com os métodos do ciclo de vida e as taxas de quadros da interface do usuário (cuidado com os avisos no log do coreógrafo).
Stevie
1
@ Stevie como definir a prioridade? nem runnble nem o ExecutorService têm tal método um setter
JK
1
@JK Forneça ao ExecutorService um ThreadFactory personalizado e chame Thread.setPriority no thread antes de devolvê-lo.
Stevie
1
"O uso de Threads diretamente no Android é desencorajado. Causa mais problemas do que resolve" Cuidado para elaborar isso? Na verdade, o AsyncTask está sendo preterido exatamente por isso ... techyourchance.com/asynctask-deprecated
Fran Marzoa
170

A resposta aceita tem algumas desvantagens significativas. Não é aconselhável usar o AsyncTask para redes, a menos que você realmente saiba o que está fazendo. Algumas das desvantagens incluem:

  • As AsyncTask criadas como classes internas não estáticas têm uma referência implícita ao objeto Activity, seu contexto e toda a hierarquia View criada por essa atividade. Esta referência impede que a Atividade seja coletada como lixo até que o trabalho em segundo plano do AsyncTask seja concluído. Se a conexão do usuário for lenta e / ou o download for grande, esses vazamentos de memória de curto prazo podem se tornar um problema - por exemplo, se a orientação mudar várias vezes (e você não cancelar as tarefas em execução) ou o usuário sai da atividade.
  • O AsyncTask possui características de execução diferentes, dependendo da plataforma em que é executado: antes do nível 4 da API, o AsyncTasks é executado em série em um único encadeamento em segundo plano; do nível 4 da API ao nível 10 da API, as AsyncTasks são executadas em um pool de até 128 threads; a partir do nível 11 da API, o AsyncTask é executado em série em um único encadeamento em segundo plano (a menos que você use o executeOnExecutormétodo sobrecarregado e forneça um executor alternativo). O código que funciona bem quando executado em série no ICS pode ser interrompido quando executado simultaneamente no Gingerbread, digamos se você tiver dependências inadvertidas da ordem de execução.

Se você deseja evitar vazamentos de memória de curto prazo, ter características de execução bem definidas em todas as plataformas e ter uma base para criar um gerenciamento de rede realmente robusto, convém considerar:

  1. Usando uma biblioteca que faz um bom trabalho para você - há uma boa comparação de bibliotecas de rede nesta pergunta ou
  2. Usando a Serviceou IntentService, talvez com a PendingIntentpara retornar o resultado através do onActivityResultmétodo da atividade .

Abordagem IntentService

Desvantagens:

  • Mais código e complexidade do que AsyncTask, embora não tanto quanto você imagina
  • Enfileirará solicitações e as executará em um único encadeamento em segundo plano. Você pode controlar isso facilmente, substituindo-o IntentServicepor uma Serviceimplementação equivalente , talvez como esta .
  • Hum, eu não consigo pensar em nenhum outro agora

Parte superior:

  • Evita o problema de vazamento de memória a curto prazo
  • Se a sua atividade reiniciar enquanto as operações da rede estiverem em andamento, ainda poderá receber o resultado do download através do onActivityResultmétodo
  • Plataforma melhor que o AsyncTask para criar e reutilizar códigos de rede robustos. Exemplo: se você precisar fazer um upload importante, poderá fazê-lo AsyncTaskem um Activity, mas se o contexto do usuário sair do aplicativo para atender uma ligação, o sistema poderá encerrá-lo antes que o upload seja concluído. É menos provável que mate um aplicativo com um ativo Service.
  • Se você usar sua própria versão simultânea IntentService(como a que eu vinculei acima), poderá controlar o nível de simultaneidade via Executor.

Resumo de implementação

Você pode implementar um IntentServicepara executar downloads em um único thread em segundo plano com bastante facilidade.

Etapa 1: Crie um IntentServicepara executar o download. Você pode dizer o que fazer o download via Intentextra e passá-lo PendingIntentpara usar para retornar o resultado para Activity:

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class DownloadIntentService extends IntentService {

    private static final String TAG = DownloadIntentService.class.getSimpleName();

    public static final String PENDING_RESULT_EXTRA = "pending_result";
    public static final String URL_EXTRA = "url";
    public static final String RSS_RESULT_EXTRA = "url";

    public static final int RESULT_CODE = 0;
    public static final int INVALID_URL_CODE = 1;
    public static final int ERROR_CODE = 2;

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

        // make one and re-use, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
        InputStream in = null;
        try {
            try {
                URL url = new URL(intent.getStringExtra(URL_EXTRA));
                IllustrativeRSS rss = parser.parse(in = url.openStream());

                Intent result = new Intent();
                result.putExtra(RSS_RESULT_EXTRA, rss);

                reply.send(this, RESULT_CODE, result);
            } catch (MalformedURLException exc) {
                reply.send(INVALID_URL_CODE);
            } catch (Exception exc) {
                // could do better by treating the different sax/xml exceptions individually
                reply.send(ERROR_CODE);
            }
        } catch (PendingIntent.CanceledException exc) {
            Log.i(TAG, "reply cancelled", exc);
        }
    }
}

Etapa 2: registre o serviço no manifesto:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

Etapa 3: Invoque o serviço da Atividade, passando um objeto PendingResult que o Serviço usará para retornar o resultado:

PendingIntent pendingResult = createPendingResult(
    RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);

Etapa 4: manipular o resultado em onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
        switch (resultCode) {
            case DownloadIntentService.INVALID_URL_CODE:
                handleInvalidURL();
                break;
            case DownloadIntentService.ERROR_CODE:
                handleError(data);
                break;
            case DownloadIntentService.RESULT_CODE:
                handleRSS(data);
                break;
        }
        handleRSS(data);
    }
    super.onActivityResult(requestCode, resultCode, data);
}

Um projeto do Github que contém um projeto completo do Android-Studio / Gradle está disponível aqui .

Stevie
fonte
O IntentService é a maneira correta de fazer isso, não a desenraizamento porque o AsyncTask é exatamente o caminho para não fazer isso.
Brill Pappin
3
@BrillPappin Eu concordo quase inteiramente e reformulei para enfatizar as desvantagens do AsyncTask. (Ainda acho que há um número muito pequeno de casos em que - se você realmente sabe o que está fazendo - pode ser bom usar o AsyncTask, mas a resposta aceita não mostra nenhuma desvantagem e é popular demais para o bem do Android).
Stevie
1
Você realmente precisa IllustrativeRSS? E se você não estiver trabalhando com coisas de RSS?
Cullub
Na minha opinião, o Google deveria alterar sua implementação ruim da coleta de lixo, em vez de colocar o problema no lado dos programadores. Essa é a responsabilidade do sistema operacional. Se um programador usar o IntentService ou Service para fazer o trabalho por causa do problema fundamental na implementação do Android, depois de alguns anos o Google dirá que o IntentService também é uma prática ruim e sugere outra coisa. E essa história continua ... Então, os desenvolvedores do Google devem resolver o gerenciamento de memória ruim do Android, não os programadores.
Saeed khalafinejad
Vou apenas dizer: Esta resposta parece ser mais uma maneira de compartilhar o projeto do que uma alternativa melhor AsyncTask. O erro foi feito para impedir que os desenvolvedores atrasassem a interface do usuário, não necessariamente os levou a arriscar a segurança com várias intenções / serviços.
Carrinho abandonado
144

Você não pode executar E / S de rede no thread da interface do usuário no Honeycomb . Tecnicamente, isso é possível nas versões anteriores do Android, mas é uma péssima idéia, pois fará com que o aplicativo pare de responder e pode resultar no sistema operacional que mata o seu aplicativo por se comportar mal. Você precisará executar um processo em segundo plano ou usar o AsyncTask para executar sua transação de rede em um thread em segundo plano.

Há um artigo sobre o Painless Threading no site do desenvolvedor do Android, que é uma boa introdução a isso e fornecerá uma resposta muito melhor do que a fornecida realisticamente aqui.

Mark Allison
fonte
76
  1. Não use strictMode (apenas no modo de depuração)
  2. Não altere a versão do SDK
  3. Não use um thread separado

Use Service ou AsyncTask

Veja também a questão Stack Overflow:

android.os.NetworkOnMainThreadException enviando um email do Android

venérgico
fonte
8
Talvez valha a pena enfatizar que, se você usar um Serviço, ainda precisará criar um thread separado - os retornos de chamada do Serviço são executados no thread principal. Um IntentService, por outro lado, executa seu método onHandleIntent em um encadeamento em segundo plano.
Stevie
você não deve usar um AsyncTask para operações de execução longa! As diretrizes especificam 2 a 3 segundos no máximo.
Dage
76

Execute as ações de rede em outro segmento

Por exemplo:

new Thread(new Runnable(){
    @Override
    public void run() {
        // Do network action in this function
    }
}).start();

E adicione isso ao AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
henry4343
fonte
5
Mas como podemos descobrir quando o encadeamento termina para que possamos executar o próximo conjunto de tarefas no encadeamento da interface do usuário? O AsyncTask fornece a facilidade para fazer isso. Existe uma maneira de fazer o mesmo usando threads executáveis?
Piyush Soni
3
Ele irá processar o seu código passo a passo, para que no final do código é terminar, você precisa usar o manipulador de volta à UI fio
henry4343
1
Você pode usar o serviço de intenção ou tarefa assíncrona, porque é executado no encadeamento de trabalho.
Chetan Chaudhari
63

Você desabilita o modo estrito usando o seguinte código:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

Isso não é recomendado : use a AsyncTaskinterface.

Código completo para ambos os métodos

Sandeep
fonte
2
Sim, o erro ANR viria. significa que o aplicativo não responde em 5 segundos.
Muhammad Mubashir
12
Esta é uma resposta muito ruim. Você não deve alterar a política do thread, mas escrever um código melhor: não faça operações de rede no thread principal!
shkschneider
@ Sandeep Você e outros espectadores também devem ler isso. stackoverflow.com/a/18335031/3470479
Prakhar1001
53

Operações baseadas em rede não podem ser executadas no thread principal. Você precisa executar todas as tarefas baseadas em rede em um thread filho ou implementar o AsyncTask.

É assim que você executa uma tarefa em um thread filho:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
Dhruv Jindal
fonte
1
Runnable anônimo NÃO é a melhor maneira, pois possui uma referência implícita à classe envolvente e impede que ela seja editada no GC até que o encadeamento seja concluído! Além disso, esse encadeamento será executado na mesma prioridade que o encadeamento principal / EUA, contendendo com os métodos do ciclo de vida e as taxas de quadros da interface do usuário!
Yousha Aleayoub
49

Coloque seu código dentro:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

Ou:

class DemoTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... arg0) {
        //Your implementation
    }

    protected void onPostExecute(Void result) {
        // TODO: do something with the feed
    }
}
Vaishali Sutariya
fonte
O segundo será melhor que o primeiro para a API acima de 11 #
Rohit Goswami
46

Isso acontece no Android 3.0 e acima. A partir do Android 3.0 e superior, eles restringiram o uso de operações de rede (funções que acessam a Internet) da execução no thread principal / thread da interface do usuário (o que gera seus métodos de criação e continuação na atividade).

Isso é para incentivar o uso de threads separados para operações de rede. Consulte AsyncTask para obter mais detalhes sobre como executar atividades de rede da maneira certa.

raihan ahmed
fonte
46

Usar anotações do Android é uma opção. Isso permitirá que você simplesmente execute qualquer método em um thread em segundo plano:

// normal method
private void normal() {
    doSomething(); // do something in background
}

@Background
protected void doSomething() 
    // run your networking code here
}

Observe que, embora ofereça benefícios de simplicidade e legibilidade, tem suas desvantagens.

Oleksiy
fonte
6
@Gavriel cria duplicatas de tudo o que você anota, seja um método, atividade, fragmento, singleton etc. Também pode ter alguns problemas devido a erros na biblioteca. Depurar e encontrar erros se tornaria mais difícil.
19615 Oleksiy
43

O erro ocorre devido à execução de operações de execução longa no thread principal. Você pode corrigir facilmente o problema usando AsynTask ou Thread . Você pode fazer o check-out desta biblioteca AsyncHTTPClient para obter um melhor tratamento.

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {

    @Override
    public void onStart() {
        // Called before a request is started
    }

    @Override
    public void onSuccess(int statusCode, Header[] headers, byte[] response) {
        // Called when response HTTP status is "200 OK"
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
        // Called when response HTTP status is "4XX" (for example, 401, 403, 404)
    }

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});
Ashwin S Ashok
fonte
42

Você não deve executar nenhuma tarefa demorada no encadeamento principal (encadeamento da UI), como qualquer operação de rede, E / S de arquivo ou operações de banco de dados SQLite. Portanto, para esse tipo de operação, você deve criar um encadeamento de trabalho, mas o problema é que não é possível executar diretamente nenhuma operação relacionada à interface do usuário a partir do encadeamento de trabalho. Para isso, você deve usar Handlere passar o Message.

Para simplificar todas estas coisas, Android fornece várias maneiras, como AsyncTask, AsyncTaskLoader, CursorLoaderou IntentService. Então você pode usar qualquer um deles de acordo com seus requisitos.

Kapil Vats
fonte
40

A resposta superior do spektom funciona perfeitamente.

Se você estiver escrevendo o AsyncTaskinline e não estendendo como uma classe, além disso, se houver necessidade de obter uma resposta AsyncTask, pode-se usar o get()método como abaixo.

RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();

(Do exemplo dele.)

sivag1
fonte
5
usando get()é uma má idéia ... faz AsyncTask "sync" novamente
Selvin
Existe uma maneira melhor e diferente de fazer isso? @Selvin
sivag1 20/09/2013
2
Acho que você poderia informar o tópico principal sobre o resultado. Por exemplo, envie uma transmissão para o tópico principal, incluindo o resultado.
Gary Gary
32

Isso é lançado somente para aplicativos direcionados ao SDK do Honeycomb ou superior. Os aplicativos direcionados para as versões anteriores do SDK podem fazer networking em seus encadeamentos de loop de eventos principais.

O erro é o aviso do SDK!

perada
fonte
28

Para mim foi isso:

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

O dispositivo em que eu estava testando meu aplicativo era 4.1.2, que é o SDK versão 16!

Verifique se a versão de destino é igual à sua Biblioteca de destino do Android. Se você não souber qual é a sua biblioteca de destino, clique com o botão direito do mouse em Projeto -> Caminho da Construção -> Android , e deve ser a que está marcada.

Além disso, como outros já mencionaram, inclua as permissões corretas para acessar a Internet:

<uses-permission android:name="android.permission.INTERNET"/>
rharvey
fonte
11
Deixe-me explicar o que você está fazendo aqui: NetworkOnMainThreadExceptioné o Guardian que está lhe dizendo: não atire no seu próprio pé ... sua solução é: vamos voltar ao passado quando não havia Guardian - agora eu posso atirar no meu pé livremente
Selvin
1
Também adotei essa abordagem e não tive nenhum problema. Guardião às vezes é muito exigente.
FractalBob 25/10
25

Use isso em sua atividade

    btnsub.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    //Initialize soap request + add parameters
                    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);

                    //Use this to add parameters
                    request.addProperty("pincode", txtpincode.getText().toString());
                    request.addProperty("bg", bloodgroup.getSelectedItem().toString());

                    //Declare the version of the SOAP request
                    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

                    envelope.setOutputSoapObject(request);
                    envelope.dotNet = true;

                    try {
                        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

                        //this is the actual part that will call the webservice
                        androidHttpTransport.call(SOAP_ACTION1, envelope);

                        // Get the SoapResult from the envelope body.
                        SoapObject result = (SoapObject) envelope.getResponse();
                        Log.e("result data", "data" + result);
                        SoapObject root = (SoapObject) result.getProperty(0);
                        // SoapObject s_deals = (SoapObject) root.getProperty(0);
                        // SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
                        //

                        System.out.println("********Count : " + root.getPropertyCount());

                        value = new ArrayList<Detailinfo>();

                        for (int i = 0; i < root.getPropertyCount(); i++) {
                            SoapObject s_deals = (SoapObject) root.getProperty(i);
                            Detailinfo info = new Detailinfo();

                            info.setFirstName(s_deals.getProperty("Firstname").toString());
                            info.setLastName(s_deals.getProperty("Lastname").toString());
                            info.setDOB(s_deals.getProperty("DOB").toString());
                            info.setGender(s_deals.getProperty("Gender").toString());
                            info.setAddress(s_deals.getProperty("Address").toString());
                            info.setCity(s_deals.getProperty("City").toString());
                            info.setState(s_deals.getProperty("State").toString());
                            info.setPinecode(s_deals.getProperty("Pinecode").toString());
                            info.setMobile(s_deals.getProperty("Mobile").toString());
                            info.setEmail(s_deals.getProperty("Email").toString());
                            info.setBloodgroup(s_deals.getProperty("Bloodgroup").toString());
                            info.setAdddate(s_deals.getProperty("Adddate").toString());
                            info.setWaight(s_deals.getProperty("waight").toString());
                            value.add(info);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(getApplicationContext(), ComposeMail.class);
                    //intent.putParcelableArrayListExtra("valuesList", value);

                    startActivity(intent);
                }
            }).start();
        }
    });
dhiraj kakran
fonte
23

Apenas para esclarecer algo explicitamente:

O thread principal é basicamente o thread da interface do usuário.

Dizer que você não pode realizar operações de rede no encadeamento principal significa que não pode realizar operações de rede no encadeamento da interface do usuário, o que significa que não é possível realizar operações de rede em um *runOnUiThread(new Runnable() { ... }*bloco dentro de outro encadeamento.

(Eu tive um longo momento de coçar a cabeça tentando descobrir por que estava recebendo esse erro em outro lugar que não o meu thread principal. Foi por isso; esse thread ajudou; e espero que esse comentário ajude outra pessoa.)

Novak
fonte
22

Essa exceção ocorre devido a qualquer tarefa pesada executada no encadeamento principal, se essa tarefa em execução demorar muito tempo .

Para evitar isso, podemos lidar com isso usando threads ou executores

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});
amardeep
fonte
18

Já existem muitas ótimas respostas para essa pergunta, mas muitas grandes bibliotecas surgiram desde que essas respostas foram postadas. Este é um tipo de guia para iniciantes.

Vou cobrir vários casos de uso para executar operações de rede e uma ou duas soluções para cada uma.

ReST sobre HTTP

Tipicamente Json, pode ser XML ou outra coisa

Acesso completo à API

Digamos que você esteja escrevendo um aplicativo que permita aos usuários acompanhar os preços das ações, taxas de juros e taxas de câmbio. Você encontra uma API Json que se parece com isso:

http://api.example.com/stocks                       //ResponseWrapper<String> object containing a list of Srings with ticker symbols
http://api.example.com/stocks/$symbol               //Stock object
http://api.example.com/stocks/$symbol/prices        //PriceHistory<Stock> object
http://api.example.com/currencies                   //ResponseWrapper<String> object containing a list of currency abbreviation
http://api.example.com/currencies/$currency         //Currency object
http://api.example.com/currencies/$id1/values/$id2  //PriceHistory<Currency> object comparing the prices of the first currency (id1) to the second (id2)

Retrofit from Square

Essa é uma excelente opção para uma API com vários pontos de extremidade e permite declarar os pontos de extremidade do ReST em vez de precisar codificá-los individualmente, como acontece com outras bibliotecas como ion ou Volley. (site: http://square.github.io/retrofit/ )

Como você o usa com a API de finanças?

build.gradle

Adicione estas linhas ao seu buid.gradle no nível do módulo:

implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version

FinancesApi.java

public interface FinancesApi {
    @GET("stocks")
    Call<ResponseWrapper<String>> listStocks();
    @GET("stocks/{symbol}")
    Call<Stock> getStock(@Path("symbol")String tickerSymbol);
    @GET("stocks/{symbol}/prices")
    Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol);

    @GET("currencies")
    Call<ResponseWrapper<String>> listCurrencies();
    @GET("currencies/{symbol}")
    Call<Currency> getCurrency(@Path("symbol")String currencySymbol);
    @GET("currencies/{symbol}/values/{compare_symbol}")
    Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst);
}

FinancesApiBuilder

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

Fragmento de fragmento

FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
    @Override
    public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
        Stock stock = stockCall.body();
        //do something with the stock
    }
    @Override
    public void onResponse(Call<Stock> stockCall, Throwable t){
        //something bad happened
    }
}

Se sua API exigir que uma chave de API ou outro cabeçalho como um token de usuário etc. seja enviado, o Retrofit facilita isso (consulte esta resposta incrível para obter detalhes: https://stackoverflow.com/a/42899766/1024412 ).

Acesso único à API do ReST

Digamos que você esteja criando um aplicativo "clima ameno" que pesquise a localização do GPS do usuário e verifique a temperatura atual nessa área e informe o clima. Esse tipo de aplicativo não precisa declarar pontos de extremidade da API; ele só precisa acessar um ponto de extremidade da API.

Íon

Esta é uma ótima biblioteca para esse tipo de acesso.

Leia a excelente resposta de msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Carregar imagens via HTTP

Vôlei

O Volley também pode ser usado para APIs ReST, mas devido à configuração mais complicada necessária, eu prefiro usar o Retrofit from Square como acima ( http://square.github.io/retrofit/ )

Digamos que você esteja criando um aplicativo de rede social e deseje carregar fotos de perfil de amigos.

build.gradle

Adicione esta linha ao seu nível de módulo buid.gradle:

implementation 'com.android.volley:volley:1.0.0'

ImageFetch.java

O Volley requer mais configuração que o Retrofit. Você precisará criar uma classe como esta para configurar um RequestQueue, um ImageLoader e um ImageCache, mas não é tão ruim assim:

public class ImageFetch {
    private static ImageLoader imageLoader = null;
    private static RequestQueue imageQueue = null;

    public static ImageLoader getImageLoader(Context ctx){
        if(imageLoader == null){
            if(imageQueue == null){
                imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
                Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
        return imageLoader;
    }
}

user_view_dialog.xml

Adicione o seguinte ao arquivo xml de layout para adicionar uma imagem:

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/profile_picture"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    app:srcCompat="@android:drawable/spinner_background"/>

UserViewDialog.java

Adicione o seguinte código ao método onCreate (Fragment, Activity) ou ao construtor (Dialog):

NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());

Picasso

Outra excelente biblioteca da Square. Consulte o site para alguns ótimos exemplos: http://square.github.io/picasso/

KG6ZVP
fonte
16

Em palavras simples,

NÃO TRABALHE EM REDE NA LINHA DA UI

Por exemplo, se você fizer uma solicitação HTTP, essa é uma ação de rede.

Solução:

  1. Você precisa criar um novo thread
  2. Ou use a classe AsyncTask

Caminho:

Coloque todos os seus trabalhos dentro

  1. run() método de nova discussão
  2. Ou doInBackground() método da classe AsyncTask.

Mas:

Quando você obtém algo da resposta de rede e deseja mostrá-lo em sua exibição (como exibir mensagem de resposta no TextView), é necessário retornar ao thread da interface do usuário .

Se você não fizer isso, você receberá ViewRootImpl$CalledFromWrongThreadException.

Como?

  1. Ao usar o AsyncTask, atualize a exibição do onPostExecute()método
  2. Ou chame o runOnUiThread()método e atualize a exibição dentro do run()método.
Nabin
fonte
12

Você pode mover uma parte do seu código para outro thread para descarregar main threade evitar ANR , NetworkOnMainThreadException , IllegalStateException (por exemplo, não é possível acessar o banco de dados no thread principal, pois pode bloquear a interface do usuário por um longo período de tempo).

Existem algumas abordagens que você deve escolher depende da situação

Thread Java ou Android HandlerThread

Os encadeamentos Java são usados ​​apenas uma vez e morrem após a execução de seu método de execução.

HandlerThread é uma classe útil para iniciar um novo thread que possui um looper.

AsyncTask

O AsyncTask foi projetado para ser uma classe auxiliar em torno do Thread and Handler e não constitui uma estrutura de encadeamento genérica. Idealmente, as AsyncTasks devem ser usadas para operações curtas (no máximo, alguns segundos). Se você precisar manter os threads em execução por longos períodos, é altamente recomendável usar as várias APIs fornecidas pelo pacote java.util.concurrent, como Executor , ThreadPoolExecutor e FutureTask .

Implementação do conjunto de encadeamentos ThreadPoolExecutor , ScheduledThreadPoolExecutor ...

Classe ThreadPoolExecutor que implementa ExecutorService, que fornece controle fino sobre o conjunto de encadeamentos (por exemplo, tamanho do conjunto principal, tamanho máximo do conjunto, tempo de vida útil etc.)

ScheduledThreadPoolExecutor - uma classe que estende o ThreadPoolExecutor. Ele pode agendar tarefas após um determinado atraso ou periodicamente.

FutureTask

FutureTask executa processamento assíncrono; no entanto, se o resultado ainda não estiver pronto ou o processamento não estiver concluído, a chamada de get () bloqueará o encadeamento.

AsyncTaskLoaders

AsyncTaskLoaders, pois resolvem muitos problemas inerentes ao AsyncTask

IntentService

Esta é a opção padrão para o processamento de longa execução no Android; um bom exemplo seria o upload ou o download de arquivos grandes. O upload e o download podem continuar mesmo se o usuário sair do aplicativo e você certamente não desejar impedir o usuário de usá-lo enquanto essas tarefas estiverem em andamento.

JobScheduler

Efetivamente, você precisa criar um Serviço e criar um trabalho usando JobInfo.Builder que especifique seus critérios para quando executar o serviço.

RxJava

Biblioteca para compor programas assíncronos e baseados em eventos usando sequências observáveis.

Corotinas (Kotlin)

O principal é que ele faz com que o código assíncrono se pareça com o síncrono

Leia mais aqui , aqui , aqui , aqui

yoAlex5
fonte
Trabalhou para mim ... Eu usei o AsyncTask extensivamente, mas quando uma tarefa está em execução, outra espera. Eu quero resolver isso. Agora trabalhando com executeonexecutor. Vamos ver como ele se comportará em dispositivos com pouca memória.
Suraj Shingade
Por favor, dê uma olhada no método: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); executar sua tarefa simultaneamente
yoAlex5
10

Embora acima haja um enorme pool de soluções, ninguém mencionou com.koushikdutta.ion: https://github.com/koush/ion

Também é assíncrono e muito simples de usar:

Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
   @Override
    public void onCompleted(Exception e, JsonObject result) {
        // do stuff with the result or error
    }
});
msysmilu
fonte
10

As soluções novas Threade AsyncTask já foram explicadas.

AsyncTaskidealmente deve ser usado para operações curtas. NormalThread não é preferível para Android.

Dê uma olhada na solução alternativa usando HandlerThread e Handler

HandlerThread

Classe útil para iniciar um novo thread que possui um looper. O looper pode então ser usado para criar classes de manipuladores. Observe que start()ainda deve ser chamado.

Manipulador:

Um manipulador permite enviar e processar objetos Message e Runnable associados ao MessageQueue de um thread. Cada instância do manipulador está associada a um único encadeamento e à fila de mensagens desse encadeamento. Quando você cria um novo manipulador, ele é vinculado à fila de encadeamentos / mensagens do encadeamento que o está criando - a partir desse momento, ele envia mensagens e executáveis ​​para essa fila de mensagens e os executa à medida que saem da mensagem fila.

Solução:

  1. Crio HandlerThread

  2. Ligue start()paraHandlerThread

  3. Crie Handlerobtendo LooperdeHanlerThread

  4. Incorpore o código relacionado à operação de rede no Runnableobjeto

  5. Enviar Runnabletarefa paraHandler

Exemplo de trecho de código, que endereço NetworkOnMainThreadException

HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Ravi", "Before IO call");
            URL page = new URL("http://www.google.com");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ( (line =  buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Ravi", "After IO call");
            Log.d("Ravi",text.toString());

        }catch( Exception err){
            err.printStackTrace();
        }
    }
};
mainHandler.post(myRunnable);

Prós de usar esta abordagem:

  1. Criar novo Thread/AsyncTaskpara cada operação de rede é caro. O Thread/AsyncTaskserá destruído e recriado para as próximas operações de rede. Mas com Handlere com a HandlerThreadabordagem, você pode enviar muitas operações de rede (como tarefas executáveis) para uma única HandlerThreadusando Handler.
Ravindra babu
fonte
8

RxAndroidé outra alternativa melhor para esse problema e evita aborrecimentos na criação de threads e na publicação de resultados no thread da interface do usuário do Android. Nós apenas precisamos especificar threads nos quais as tarefas precisam ser executadas e tudo é tratado internamente.

Observable<List<String>> musicShowsObservable = Observable.fromCallable(new Callable<List<String>>() { 

  @Override 
  public List<String> call() { 
    return mRestClient.getFavoriteMusicShows(); 
  }
});

mMusicShowSubscription = musicShowsObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<String>>() {

    @Override 
    public void onCompleted() { }

    @Override 
    public void onError(Throwable e) { }

    @Override 
    public void onNext(List<String> musicShows){
        listMusicShows(musicShows);
    }
});
  1. , Especificando (Schedulers.io()), RxAndroid será executado getFavoriteMusicShows() em um segmento diferente.

  2. Ao usar AndroidSchedulers.mainThread(), queremos observar este Observável no thread da interface do usuário, ou seja, queremos que nosso onNext()retorno de chamada seja chamado no thread da interface do usuário

Shinoo Goyal
fonte
8

O thread principal é o thread da interface do usuário e você não pode executar uma operação no thread principal que possa bloquear a interação do usuário. Você pode resolver isso de duas maneiras:

Forçar a executar a tarefa no thread principal como este

StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);

Ou crie um manipulador simples e atualize o segmento principal, se desejar.

Runnable runnable;
Handler newHandler;

newHandler = new Handler();
runnable = new Runnable() {
    @Override
    public void run() {
         try {
            //update UI
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
};
newHandler.post(runnable);

E para parar o thread, use:

newHandler.removeCallbacks(runnable);

Para obter mais informações, consulte: Rosqueamento indolor

Sharath kumar
fonte
Valeu. A versão 1 ajuda ao adicionar como primeira ação no onCreate.
Ingo
7

Isso funciona. Apenas simplificou a resposta do Dr. Luiji.

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
Kacy
fonte
7

No Android, as operações de rede não podem ser executadas no thread principal. Você pode usar o Thread, AsyncTask (tarefas de execução curta), Serviço (tarefas de execução longa) para executar operações de rede.

Ponsuyambu Velladurai
fonte
7

O acesso a recursos de rede a partir do thread principal (UI) causa essa exceção. Use um thread separado ou o AsyncTask para acessar um recurso de rede para evitar esse problema.

RevanthKrishnaKumar V.
fonte