Питання Як виправити іroid.os.NetworkOnMainThreadException?


У мене виникла помилка при запуску мого проекту 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

Як вирішити цю проблему?


1981
2018-06-14 12:02


походження


Читайте це повідомлення в блозі в NetworkOnMainThreadException для отримання додаткової інформації. Це пояснює, чому це відбувається на пристроях Android 3.0 або новіших версій. - Adrian Monk
Щоб бути на обкладинці, спочатку прочитайте про мережеві запити в Android, то я б рекомендував навчатися "Volley". - Anuj Sharma
Є багато альтернативних бібліотек, які вирішують цю проблему. Багато хто з них перераховані внизу цієї сторінки. Якщо у вас більше, ми їх візьмемо :) - Snicolas
Вам потрібно запустити інтернет-активності по потоку, відокремленому від основної (інтерфейсу) потоку - Naveed Ahmad
stackoverflow.com/questions/16439587/...    вище ланка хороша - nguyenvangiangbn


Відповіді:


Це виняток викидається, коли прикладна програма намагається виконати мережеву операцію в його основному потоці. Запустіть свій код у 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"/>

2240
2018-06-14 12:15



Я думаю, що варто відзначити, що фрагмент коду вище повинен бути підкласом (внутрішній клас), бажано приватним. Таким чином, коли AsyncTask закінчується, ви все ще можете керувати нутрощами вашого класу. - dyslexicanaboko
Насправді я зробив ту саму річ, як ви сказали вище, але я стикаюся з цією помилкою java.lang.RuntimeException: Неможливо створити обробник усередині потоку, який не називався Looper.prepare () - Dhruv Tyagi
Це саме неправильна відповідь. Я постійно стикаюся з цим у коді людей, і його дратує доведеться постійно її виправити. AsyncTask не слід використовувати для активності в мережі, оскільки це пов'язано з діяльністю, але не життєвим циклом діяльності. Обертання пристрою з цим завданням призведе до винятку та призведе до аварійного завершення роботи додатка. Використовуйте IntentService, що замість даних скидає дані в базі даних sqlite. - Brill Pappin
Застереження, AsyncTask часто використовується для операцій мережі, якщо вона не повинна бути. його життєвий цикл не синхронізований з діяльністю. Для отримання даних потрібно використовувати IntentService та базу даних за видом. - Brill Pappin
Я настійно рекомендую AsyncTask Loaders для таких випадків. - Roman Gherta


Ви повинні майже завжди виконувати операції в мережі по потоку або як асинхронне завдання.

Але це є можна видалити це обмеження, і ви перевизначите за замовчуванням поведінку, якщо ви готові прийняти наслідки.

Додати:

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


557
2018-02-15 06:59



Це дуже погана ідея. рішення полягає в тому, щоб уникнути мережі IO на основній течії (як показує прийнята відповідь). - MByD
З цією метою ви лише приховуєте справжню проблему. - Alex
@TwistedUmbrella AsyncTask не додає сторінку коду, додає 6 рядків (декларація класу, заміщення анотації, doInBackground декларація, 2 закриваючих дужки і дзвінок до execute()) З іншого боку, навіть один витяг з сайту, як ви згадуєте, призводить до значного відставання в чутливості інтерфейсу користувача. Не лякайтеся. - Zoltán
@ TwistedUmbrella Не будь дурним. Ось як створюються погані програми. Будь то завантаження текстового файлу або галереї зображень, слід дотримуватися найкращих практик. І запуск мережі IO на основні потоки - це не найкраща практика. - States
Присвоєно Ця відповідь є вірною, і для багатьох програмістів, які не є ні наївними, ні дурними, а просто потребують SYNCHRONOUS (тобто: це повинен заблокувати додаток) дзвоніть, це саме те, що потрібно. Мені зовсім не подобається, що Android виключає за замовчуванням (IMHO це дуже "корисна" річ зробити!) - але я однаково рада сказати "спасибі, але - це насправді те, що я мав намір" і переопределити це - Adam


Я вирішив цю проблему за допомогою нового Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

346
2018-01-21 16:31



Замість того, щоб створювати нову нитку кожного разу, коли ви хочете виконати мережеву операцію, ви також можете скористатися послугою одного виконавця-потоку. - Alex Lockwood
Простий, але небезпечний. Анонімний Runnable має неявне посилання на клас, що містить (наприклад, вашу активність або фрагмент), запобігаючи збирання сміття до завершення потоку. Ви повинні принаймні встановити пріоритет процесу Process.BACKGROUND, інакше цей потік буде працювати з таким же пріоритетом, як і основний / ui потоку, суперечливо з методами життєвого циклу та такими частотами кадрів (стежте за попередженнями в журналі від хореографа). - Stevie
@Stevie як встановити пріоритет? Ні виконавчий, ні виконавчий служба не мають такого методу встановлення - J. K.
@ J.K. Поставте ваш ExecutorService користувальницькою ThreadFactory та зателефонуйте Thread.setPriority до потоку перед тим, як повернути його. - Stevie
Встановлення пріоритету для чогось, що призначене для здійснення сеансового дзвінка (який буде блокувати), здається надмірним. - john16384


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

Є стаття про Безболісний різьблення на сайті розробника Android, що є гарним введенням в ній, і це дасть вам набагато кращу глибину відповіді, ніж можна тут реально представити.


124
2018-06-14 12:07





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

  • AsyncTask, створений як нестатичні внутрішні класи, має неявне посилання на об'єкт, що містить додаток, його контекст та всю ієрархію перегляду, створена цією діяльністю. Ця довідка не дозволяє Агенції збирати сміття до завершення фонової роботи AsyncTask. Якщо з'єднання користувача відбувається повільно та / або завантаження велике, ці короткочасні витоки пам'яті можуть стати проблемою - наприклад, якщо орієнтація змінюється кілька разів (і ви не скасуєте виконавчі завдання), або користувач переходить від діяльності.
  • AsyncTask має різні характеристики виконання в залежності від платформи, на якій вона виконується: до рівня API 4 AsyncTasks виконується серійно на одній фонової нитки; від API-рівня 4 до API-рівня 10, AsyncTasks виконується на базі до 128 потоків; починаючи з API-рівня 11, AsyncTask виконується серійно на одному фоновому потоці (якщо ви не використовуєте перевантажені executeOnExecutorметод та альтернативний виконавець). Код, який чудово працює під час запуску серійно в ICS, може порушуватись, коли він виконується одночасно на "пряникці", скажімо, якщо у вас є випадкові залежності від порядку виконання.

Якщо ви хочете уникнути короткочасної витоку пам'яті, чітко визначте характеристики виконання на всіх платформах та мати базу для створення надійної роботи з мережею, можливо, ви захочете розглянути таке:

  1. Використовуючи бібліотеку, яка робить приємну роботу для вас - це чудове порівняння мережевих бібліотек у це питання, або
  2. Використовуючи a Service або IntentService замість цього, можливо, з a PendingIntent щоб повернути результат за допомогою активності onActivityResult метод

Намір обслуговування підходу

Знизу:

  • Більше коду і складності ніж AsyncTask, хоча і не так сильно, як ви думаєте
  • Буде запит у черзі та запустіть їх на сингл фонову нитку Ви можете легко контролювати це, замінивши його IntentService з еквівалентом Service реалізація, можливо, подобається цей.
  • О, я не можу думати про інших прямо зараз

Зверху:

  • Уникає короткочасної проблеми витікання пам'яті
  • Якщо ваша активність перезавантажується, коли операції мережі в режимі польоту, він все одно може отримати результат завантаження через його onActivityResult метод
  • Краща платформа, ніж AsyncTask, для побудови та повторного використання надійного мережевого коду. Приклад: якщо вам потрібно зробити важливу завантаження, ви можете зробити це з AsyncTask в Activity, але якщо контекст користувача - вимикає додаток, щоб здійснити телефонний дзвінок, система може вбити додаток до завершення завантаження. це є менш вірогідно вбити програму активною Service.
  • Якщо ви використовуєте власну паралельну версію IntentService (як і я, про який я зв'язався вище), ви можете контролювати рівень паралельного використання через Executor.

Резюме виконання

Ви можете реалізувати a IntentService досить легко зробити завантаження на одній фоновій гілці.

Крок 1. Створіть IntentService виконати завантаження. Ви можете сказати, що завантажити через Intent екстра, і передати його a 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. Виклик служби з Дії, передаючи об'єкт PendingResult, який Служба використовуватиме для повернення результату:

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 доступний тут.


118
2018-01-22 13:17



IntentService це правильний спосіб це зробити, а не вилучення, оскільки AsyncTask - це саме спосіб не робити це. - Brill Pappin
@ BrillPappin Я майже повністю згоден і повторював, щоб підкреслити недоліки AsyncTask. (Я все ще думаю, що є дуже мало кількість випадків, коли - якщо ви дійсно знаєте, що ви робите - це може Будьте в порядку, щоб використовувати AsyncTask, але прийнята відповідь не вказує на недоліки і надто популярна на користь Android). - Stevie
Ви дійсно потребуєте IllustrativeRSS? Що робити, якщо ви не працюєте з матеріалами RSS? - Cullub


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

Використовуйте службу або AsyncTask

Див. Також питання про переповнення стеків:

android.os.NetworkOnMainThreadException відправка електронного листа з Android


59
2017-08-18 09:18



Можливо, варто підкреслити, що якщо ви користуєтесь сервісом, вам все одно доведеться створити окрему гілку - сервісні виклики, що виконуються в основному потоці. Інтегрова служба, з іншого боку, запускає метод onHandleIntent на фоні потоку. - Stevie
ви не повинні використовувати AsyncTask для тривалих операцій! Рекомендації містять від 2 до 3 секунд макс. - Dage


Зробіть дії мережі на іншому потоці

Наприклад:

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

Додайте це до AndroidManifest.xml

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

51
2017-12-24 14:33



Але як ми можемо дізнатись, коли закінчується потік, щоб ми могли виконувати наступний набір завдань у потоці користувальницького інтерфейсу? AsyncTask забезпечує об'єкт для цього. Чи є спосіб зробити те ж саме, використовуючи непрохідні нитки? - Piyush Soni
Він буде обробляти ваш код крок за кроком, так що в кінці коду закінчено, вам потрібно використовувати обробник назад у потоці інтерфейсу користувача - henry4343
mobileorchard.com/... - henry4343
Ви можете використовувати асинхронне завдання або намір служби, тому що це виконується на робочому потоці. - Chetan Chaudhari


Ви вимкніть суворий режим, використовуючи наступний код:

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

Це не рекомендується: використовувати AsyncTaskінтерфейс

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


46
2017-10-24 07:10



Так стане помилка ANR. означає, що програма не відповідає за 5 сек. - Muhammad Mubashir
Це дуже погана відповідь. Ви не повинні змінювати політику потоку, а писати кращий код: не виконувати операції мережі на основному потоці! - shkschneider
Не добре, але корисно для POC / Налагодження - hB0
@Sandeep Ви та інші глядачі повинні це прочитати. stackoverflow.com/a/18335031/3470479 - Prakhar1001


Операції на базі мережі не можуть бути запущені в основному потоці. Вам потрібно запустити всі мережеві завдання на дочірній потік або запустити AsyncTask.

Ось як ви запускаєте завдання в дитячому потоці:

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

39
2018-01-14 06:14



Анонімний Runnable - це НЕ найкращий спосіб, оскільки воно має неявне посилання на клас, який додає, і не дозволяє йому бути GC ed до моменту завершення потоку! Також цей потік буде працювати на тому ж пріоритеті, що й основний / американський потік, суперечать методам життєвого циклу та частоті кадрів інтерфейсу користувача! - Yousha Aleayoub


Вставте свій код:

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
    }
}

37
2017-07-28 10:43



Другий буде найкращим, ніж спочатку для api вище 11 - Rohit Goswami
прочитайте коментар у розділі: stackoverflow.com/a/21107128/1429432 - Yousha Aleayoub
Async працювати в більшості випадків - Ajay Pandya