Как я могу исправить android.os.NetworkOnMainThreadException? - PullRequest
2239 голосов
/ 14 июня 2011

Я получил ошибку при запуске моего проекта Android для RssReader.

Код:

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();

И это показывает ошибку ниже:

android.os.NetworkOnMainThreadException

Как я могу исправить эту проблему?

Ответы [ 53 ]

2442 голосов
/ 14 июня 2011

Это исключение выдается, когда приложение пытается выполнить сетевую операцию в своем основном потоке.Запустите ваш код в 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
    }
}

Как выполнить задачу:

В MainActivity.java файле вы можете добавить эту строку в ваш oncreate()метод

new RetrieveFeedTask().execute(urlToRssFeed);

Не забудьте добавить это в AndroidManifest.xml файл:

<uses-permission android:name="android.permission.INTERNET"/>
630 голосов
/ 15 февраля 2012

Вы почти всегда должны запускать сетевые операции в потоке или как асинхронную задачу.

Но возможно , чтобы удалить это ограничение, и вы переопределяете значение по умолчанию.поведение, если вы готовы принять последствия.

Добавить:

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

StrictMode.setThreadPolicy(policy); 

В вашем классе,

и

ДОБАВИТЬ это разрешение в Androidфайл manifest.xml:

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

Последствия:

Ваше приложение (в местах с нестабильным подключением к Интернету) перестает отвечать на запросы и блокируется, пользователь воспринимает медлительность и вынужден принудительно убиватьи вы рискуете, что менеджер активности убьет ваше приложение и сообщит пользователю, что приложение остановлено.

В Android есть несколько полезных советов по передовым методам программирования для разработки для быстрого реагирования: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html

396 голосов
/ 21 января 2013

Я решил эту проблему, используя новый Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 
148 голосов
/ 22 января 2014

Принятый ответ имеет несколько существенных недостатков. Не рекомендуется использовать AsyncTask для работы в сети, если вы действительно не знаете, что делаете. Некоторые из недостатков включают в себя:

  • AsyncTask, созданные как нестатические внутренние классы, имеют неявную ссылку на включающий объект Activity, его контекст и всю иерархию View, созданную этим действием. Эта ссылка предотвращает сбор мусора в Activity, пока не завершится фоновая работа AsyncTask. Если подключение пользователя медленное и / или загрузка велика, эти кратковременные утечки памяти могут стать проблемой - например, если ориентация меняется несколько раз (и вы не отменяете выполнение задач), или пользователь перемещается от деятельности.
  • AsyncTask имеет различные характеристики выполнения в зависимости от платформы, на которой он выполняется: до уровня API 4 AsyncTask выполняется последовательно в одном фоновом потоке; от уровня API 4 до уровня API 10 AsyncTasks выполняется в пуле до 128 потоков; начиная с уровня API 11 и далее AsyncTask выполняется последовательно в одном фоновом потоке (если вы не используете перегруженный метод executeOnExecutor и не предоставляете альтернативного исполнителя). Код, который отлично работает при последовательном запуске на ICS, может прерваться при одновременном выполнении на Gingerbread, например, если у вас есть непреднамеренные зависимости порядка выполнения.

Если вы хотите избежать кратковременных утечек памяти, иметь четко определенные характеристики выполнения на всех платформах и иметь базу для построения действительно надежной обработки сети, вы можете рассмотреть следующие вопросы:

  1. Использование библиотеки, которая делает эту работу за вас - есть хорошее сравнение сетевых библиотек в этом вопросе или
  2. Вместо этого используйте Service или IntentService, возможно, с PendingIntent, чтобы вернуть результат методом onActivityResult Действия.

IntentService подход

вниз-сторона:

  • Больше кода и сложности, чем AsyncTask, но не так много, как вы думаете
  • Будет помещать запросы в очередь и запускать их в одном фоновом потоке. Вы можете легко контролировать это, заменив IntentService эквивалентной Service реализацией, возможно, такой как this .
  • Хм, сейчас я не могу думать ни о каких других

Up-сторон:

  • Предотвращает проблему кратковременной утечки памяти
  • Если ваша деятельность возобновляется, когда сетевые операции находятся в полете, она все равно может получить результат загрузки с помощью onActivityResult метода
  • Лучшая платформа, чем AsyncTask, для создания и повторного использования надежного сетевого кода. Пример: если вам нужно сделать важную загрузку, вы можете сделать это с AsyncTask в Activity, но если пользователь переключается из приложения, чтобы принять телефонный звонок, система может убить приложение до завершения загрузки. менее вероятно убить приложение с активным Service.
  • Если вы используете свою собственную параллельную версию IntentService (например, ту, которую я упомянул выше), вы можете контролировать уровень параллелизма с помощью Executor.

Краткое описание реализации

Вы можете легко реализовать IntentService для загрузки в одном фоновом потоке.

Шаг 1: Создайте IntentService для выполнения загрузки. Вы можете указать, что загружать с помощью Intent extra, и передать его PendingIntent, чтобы использовать для возврата результата в 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);
        }
    }
}

Шаг 2. Зарегистрируйте службу в манифесте:

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

Шаг 3: вызвать службу из Activity, передав объект PendingResult, который Service будет использовать для возврата результата:

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);

Шаг 4: обработать результат в 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);
}

Доступен проект github, содержащий полностью работающий проект Android-Studio / gradle здесь .

139 голосов
/ 14 июня 2011

Вы не можете выполнить сеть Ввод / вывод в потоке пользовательского интерфейса на Сота . Технически, это возможно на более ранних версиях Android, но это действительно плохая идея, поскольку это заставит ваше приложение перестать отвечать на запросы и может привести к тому, что ОС убьет ваше приложение за плохое поведение. Вам нужно будет запустить фоновый процесс или использовать AsyncTask для выполнения вашей сетевой транзакции в фоновом потоке.

На сайте разработчиков Android есть статья о Painless Threading , которая является хорошим введением в это, и она даст вам гораздо более глубокую глубину ответа, чем это можно реально представить здесь.

71 голосов
/ 18 августа 2013
  1. Не использовать строгий режим (только в режиме отладки)
  2. Не изменять версию SDK
  3. Не использовать отдельную тему

Использовать службу или AsyncTask

См. Также вопрос переполнения стека:

android.os.NetworkOnMainThreadException отправка электронного письма с Android

67 голосов
/ 24 декабря 2013

Выполнить сетевые действия в другом потоке

Например:

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

И добавить это в AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>
56 голосов
/ 24 октября 2012

Вы отключите строгий режим, используя следующий код:

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

Это не рекомендуется : используйте интерфейс AsyncTask.

Полный код для обоих методов

49 голосов
/ 14 января 2014

Сетевые операции не могут выполняться в главном потоке. Вам нужно запустить все сетевые задачи в дочернем потоке или реализовать AsyncTask.

Вот как вы запускаете задачу в дочернем потоке:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
46 голосов
/ 28 июля 2014

Введите код внутри:

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

Или:

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...