Як виправити "android.os.NetworkOnMainThreadException"?


2393

Під час запуску проекту 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

Як я можу виправити це питання?


131
Прочитайте цю публікацію в блозі на NetworkOnMainThreadException для отримання додаткової інформації. Це пояснює, чому це відбувається на Android 3.0 і вище.
Адріан Монк

6
Щоб бути на обрядовій доріжці, спочатку прочитайте про мережеві запити в android, тоді я рекомендую вивчити "Воллі".
Ануй Шарма

3
Існує багато альтернативних бібліотек, які вирішують це питання. Багато хто з них перелічено внизу цієї сторінки . Якщо у вас є більше, ми беремо їх :)
Snicolas

Вам потрібно запустити інтернет-діяльність на потоці, окремої від основної (UI) теми
Naveed Ahmad

"Через помилку в попередніх версіях Android система не позначила запис на TCP-розетку на головній нитці як порушення строгого режиму. Android 7.0 виправляє цю помилку. Програми, які проявляють таку поведінку, зараз кидають android.os. NetworkOnMainThreadException. " - Тож деякі з нас до недавнього часу цього не вражали! developer.android.com/about/versions/nougat/…
Jay

Відповіді:


2547

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

37
Я вважаю, що тут варто зазначити, що фрагмент коду, наведений вище, повинен бути підкласом (внутрішній клас), бажано приватним. Таким чином, коли AsyncTask закінчується, ви все ще можете маніпулювати внутрішніми частинами свого класу.
dislexicanaboko

4
Насправді я зробив те саме, що і у згаданому вище, але я зіткнувся з цією помилкою java.lang.RuntimeException: Не вдається створити обробник всередині потоку, який не викликав Looper.prepare ()
Dhruv Tyagi

68
Це точно неправильна відповідь. Я постійно стикаюся з цим у коді народів, і його дратує, що потрібно це постійно виправляти. AsyncTask не слід використовувати для мережевої діяльності, оскільки він прив'язаний до активності, але не життєвого циклу діяльності. Якщо поворот пристрою із цим завданням запущений, це спричинить виняток та збіть вашу програму. Використовуйте натомість IntentService, який видаляє дані в базу даних sqlite.
Брілл Паппін

5
Застереження, AsyncTask часто використовується для операцій в мережі, коли цього не повинно бути. її життєвий цикл не співпадає з діяльністю. Для отримання даних слід використовувати IntentService та базу даних, яка знаходиться за вікном перегляду.
Брілл Паппін

1
@BrillPappin, FWIW, це Посібник для розробників Android використовує AsyncTaskта піклується про зміну конфігурації.
HeyJude

676

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

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

Додати:

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


456
Це дуже погана ідея. рішення полягає в тому, щоб уникнути мережевого вводу-виводу на основний потік (як показує прийнята відповідь).
MByD

74
Цим ви лише приховуєте свою справжню проблему.
Алекс

28
@TwistedUmbrella AsyncTask не додає сторінку коду, вона додає 6 рядків (декларація класу, замітка анотації, doInBackgroundдекларація, 2 дужки закриття та виклик до execute()). З іншого боку, навіть одне завантаження сайту, як ви згадуєте, приносить значне відставання у реакції на інтерфейс користувача. Не лінуйся.
Золтан

19
Я думаю, що це ідеальне рішення, якщо ви хочете просто запустити зразковий фрагмент коду, щоб побачити, чи працює щось, перш ніж реалізувати належну AsyncTask. Ось чому я підтримав цю відповідь, хоча, як усі інші сказали, це робити не слід у виробничому додатку, а лише як швидке вирішення тесту на розробку.
зачепили82

94
Отримано. Ця відповідь є правильною, і для багатьох програмістів, які не є ні наївними, ні дурними, але просто потрібні СИНХРОННІ (тобто: це повинно блокувати додаток ) дзвінок, саме це потрібно. Я більш ніж радий, що Android за замовчуванням кидає виняток (IMHO це дуже "корисна" річ!) - але я однаково радий сказати "спасибі, але - це насправді те, що я мав намір" і перекрити це.
Адам

428

Я вирішив цю проблему, використовуючи нову Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

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

66
Простий, але небезпечний. Анонімний Runnable містить неявну посилання на клас, що вкладається (наприклад, ваша активність або фрагмент), що не дозволяє йому збирати сміття, поки нитка не завершиться. Ви повинні принаймні встановити пріоритет Process.BACKGROUND, інакше цей потік буде працювати з тим самим пріоритетом, що і основний / ui-потік, змагаючись з методами життєвого циклу та частотою кадрів ui (стежте за попередженнями в журналі хореографа).
Стіві

1
@Stevie як встановити пріоритет? ні runnble, ні виконавецьService не мають такого способу встановлення
JK

1
@JK Надайте ExecutorService користувальницьку ThreadFactory і зателефонуйте Thread.setPriority перед потоком, перш ніж повернути її.
Стіві

1
"Використання потоків безпосередньо в Android не рекомендується. Це спричиняє більше проблем, ніж вирішує". Насправді AsyncTask відміняється саме для цього ... techyourchance.com/asynctask-deprecated
Fran Marzoa

169

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

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

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

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

Підхід IntentService

Вниз:

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

Вгору:

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

Підсумок виконання

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

Крок 1. Створіть IntentServiceдля виконання завантаження. Ви можете сказати, що потрібно завантажити через Intentдодаткові, і передати це 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 доступна тут .


IntentService - це правильний спосіб зробити це, а не викорінювати, оскільки AsyncTask - це саме такий спосіб, як цього не робити.
Брілл Паппін

3
@BrillPappin Я майже повністю погоджуюся і переформулював, щоб підкреслити недоліки AsyncTask. (Я все ще думаю, що існує дуже мала кількість випадків, коли - якщо ви дійсно знаєте, що ви робите - може бути нормально використовувати AsyncTask, але прийнята відповідь не вказує на недоліки і є занадто популярною для добра Android).
Стіві

1
Вам насправді потрібно IllustrativeRSS? Що робити, якщо ви не працюєте з RSS-речами?
Cullub

На мій погляд, Google повинен змінити їхню погану реалізацію сміття, а не ставити бердон на сторону програмістів. Це відповідальність ОС. Якщо програміст використовує IntentService або Service для виконання цієї роботи через основну проблему впровадження Android, через кілька років Google скаже, що IntentService є поганою практикою і пропонує щось інше. І ця історія продовжується ... Тож розробники Google повинні вирішувати керування поганою пам'яттю Android, а не програмісти.
saeed khalafinejad

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

144

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

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


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

Скористайтеся службою або AsyncTask

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

android.os.NetworkOnMainThreadException надсилає електронний лист від Android


8
Можливо, варто наголосити на тому, що якщо ви користуєтесь Сервісом, вам все одно знадобиться створити окремий потік - Службові зворотні виклики запускаються на основний потік. З іншого боку, IntentService запускає метод onHandleIntent на фоновому потоці.
Стіві

не слід використовувати AsyncTask для тривалих операцій! В інструкціях вказано максимум від 2 до 3 секунд.
Дажжя

76

Виконайте мережеві дії на іншій потоці

Наприклад:

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

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

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

5
Але як ми можемо дізнатися, коли нитка закінчується на цьому, щоб ми могли виконати наступний набір завдань у потоці інтерфейсу? AsyncTask надає можливість зробити це. Чи можна зробити те ж саме, використовуючи запущені нитки?
Піюш Соні

3
Він буде обробляти ваш код поетапно, тому в кінці коду буде закінчено, вам потрібно використовувати обробник назад до потоку інтерфейсу користувача
henry4343

1
Ви можете використовувати завдання async або службу намірів, оскільки це виконується в робочій нитці.
Четан Чаудхарі

63

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

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

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

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


2
Так, прийде помилка ANR. означає, що додаток не відповідає на 5 сек.
Мухаммед Мубашир

11
Це дійсно погана відповідь. Ви не повинні змінювати політику потоку, а писати кращий код: не робіть мережевих операцій на головному потоці!
shkschneider

@Sandeep Ви та інші глядачі також повинні прочитати це. stackoverflow.com/a/18335031/3470479
Prakhar1001

54

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

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

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

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

49

Помістіть свій код всередині:

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

Другий з них стане кращим, ніж перший для api вище 11
Rohit Goswami

46

Це відбувається в Android 3.0 і вище. З Android 3.0 і вище вони обмежують використання мережевих операцій (функцій, які отримують доступ до Інтернету) від запуску в основному потоці / потоці користувальницького інтерфейсу (що породжується від ваших функцій створення та відновлення методів у діяльності).

Це сприяє використанню окремих потоків для мережевих операцій. Докладнішу інформацію про те, як правильно виконувати діяльність у мережі, див. У AsyncTask .


46

Використання анотацій на Android - це варіант. Це дозволить вам просто запустити будь-який метод у фоновому потоці:

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

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

Зауважимо, що, хоча вона забезпечує переваги простоти та читабельності, вона має свої недоліки.


6
@Gavriel створює дублікати всього, що ви коментуєте, будь то метод, активність, фрагмент, синглтон тощо, тому код удвічі більше, і для його складання потрібно більше часу. Також можуть виникнути деякі проблеми через помилки в бібліотеці. Налагодження та пошук помилок стали б складнішими.
Олексій

43

Помилка пов'язана з виконанням тривалих операцій в основній потоці. Ви можете легко виправити проблему за допомогою AsynTask або Thread . Ви можете перевірити цю бібліотеку AsyncHTTPClient для кращого керування.

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

42

Ви не повинні виконувати жодних трудомістких завдань на основному потоці (потоці UI), як і будь-які операції з мережею, введення / виведення файлів або операції з базою даних SQLite. Отже, для такого типу операцій слід створити робочу нитку, але проблема полягає в тому, що ви не можете безпосередньо виконати жодну операцію, пов’язану з інтерфейсом користувача, з вашої робочої нитки. Для цього вам потрібно скористатись Handlerі передати Message.

Для того, щоб спростити всі ці речі, Android надає різні способи, як AsyncTask, AsyncTaskLoader, CursorLoaderабо IntentService. Таким чином, ви можете використовувати будь-яке з них відповідно до своїх вимог.


40

Верхня відповідь spektom працює ідеально.

Якщо ви пишете AsyncTaskвбудований текст і не розширюєте його як клас, і поверх цього, якщо є необхідність отримати відповідь із AsyncTask, можна скористатися get()методом, як показано нижче.

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

(З його прикладу.)


5
використання get()- це погана ідея ... змушує AsyncTask знову "синхронізувати"
Selvin

Чи є кращий інший вихід з цього? @Selvin
sivag1

2
Я думаю, ви могли б повідомити основний потік про результат. Наприклад, надішліть трансляцію в основний потік, включаючи результат.
Chine Gary


28

Для мене це було так:

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

Пристрій, на якому я тестував свій додаток, був 4.1.2, що є SDK версії 16!

Переконайтесь, що цільова версія збігається з цільовою бібліотекою Android. Якщо ви не впевнені, яка ваша цільова бібліотека, клацніть правою кнопкою миші ваш Project -> Build Path -> Android , і вона повинна бути відміченою.

Також, як згадували інші, включіть правильні дозволи на доступ до Інтернету:

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

11
Дозвольте мені пояснити, що ви тут робите: чи NetworkOnMainThreadExceptionє опікун, який говорить вам: не стріляйте у власну ногу ... ваше рішення: повернемося до минулого, коли не було опікуна - тепер я можу стріляти в свого ноги вільно
Селвін

1
Я також застосував такий підхід і не мав жодних проблем. Опікун іноді занадто метушливий.
FractalBob

25

Використовуйте це у своїй діяльності

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

23

Просто щоб чітко прописати щось:

Основна нитка - це в основному потік інтерфейсу користувача.

Отже, кажучи, що ви не можете робити операції з мережею в основному потоці, це означає, що ви не можете робити операції з мережею в потоці інтерфейсу користувача, а значить , також не можна робити операції з мережею в *runOnUiThread(new Runnable() { ... }*блоці всередині іншого потоку.

(У мене просто був довгий момент, що чухає голову, намагаючись з'ясувати, чому я отримую цю помилку десь, крім моєї основної теми. Це було причиною; ця тема допомогла; і, сподіваюся, цей коментар допоможе комусь іншому.)


22

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

Щоб цього уникнути, ми можемо впоратися з цим за допомогою ниток або виконавців

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

18

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

Я охоплювати кілька варіантів використання для виконання мережевих операцій і в розчині , або два для кожного з них.

Відпочинок через HTTP

Зазвичай Json, це може бути XML або щось інше

Повний доступ до API

Скажімо, ви пишете додаток, який дозволяє користувачам відстежувати ціни акцій, процентні ставки та курси валют. Ви знаходите API Json, який виглядає приблизно так:

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)

Модернізація з площі

Це відмінний вибір для API з декількома кінцевими точками і дозволяє оголошувати кінцеві точки ReST, замість того, щоб кодувати їх окремо, як для інших бібліотек, таких як ion або Volley. (веб-сайт: http://square.github.io/retrofit/ )

Як ви використовуєте це з API фінансів?

build.gradle

Додайте ці рядки до свого рівня модуля buid.gradle:

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

ФінансиApiBuilder

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

Фрагмент фрагмента фінансів

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

Якщо ваш API вимагає надіслати ключ API або інший заголовок, наприклад маркер користувача тощо, Retrofit робить це легко (детальніше див. Цю чудову відповідь: https://stackoverflow.com/a/42899766/1024412 ).

Разовий доступ до API ReST

Скажімо, ви створюєте додаток "погода настрій", який шукає місцезнаходження користувачів GPS і перевіряє поточну температуру в цій області та повідомляє їм про настрій. Для цього типу додатків не потрібно оголошувати кінцеві точки API; просто потрібно мати доступ до однієї кінцевої точки API.

Іон

Це чудова бібліотека для такого типу доступу.

Прочитайте чудову відповідь msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Завантажте зображення через HTTP

Воллей

Залп можна також використовувати для API ReST, але через складніші необхідні настройки я вважаю за краще використовувати Retrofit з квадрата, як зазначено вище ( http://square.github.io/retrofit/ )

Скажімо, ви створюєте додаток для соціальних мереж і хочете завантажити фотографії профілю друзів.

build.gradle

Додайте цей рядок до свого рівня модуля buid.gradle:

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

ImageFetch.java

Воллі вимагає більше налаштувань, ніж Retrofit. Вам потрібно буде створити такий клас, щоб встановити RequestQueue, ImageLoader та ImageCache, але це не дуже погано:

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

Додайте зображення до файлу xml-файлу, щоб додати зображення:

<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

Додайте наступний код до методу onCreate (Фрагмент, Діяльність) або конструктора (Діалог):

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

Пікассо

Ще одна відмінна бібліотека від пл. Перегляньте на сайті кілька чудових прикладів: http://square.github.io/picasso/


16

Простими словами,

НЕ РОБОТИ МЕРЕЖУ У РІВНІЙ ПІШКІ

Наприклад, якщо ви робите запит HTTP, це мережева дія.

Рішення:

  1. Ви повинні створити нову Тему
  2. Або використовувати клас AsyncTask

Шлях:

Покладіть всі свої роботи всередину

  1. run() метод нової нитки
  2. Або doInBackground() метод класу AsyncTask.

Але:

Коли ви отримаєте щось із відповіді мережі та хочете показати це на своєму перегляді (наприклад, повідомлення про відповідь у TextView), вам потрібно повернутися до потоку інтерфейсу користувача .

Якщо ви цього не зробите, ви отримаєте ViewRootImpl$CalledFromWrongThreadException.

Як?

  1. Під час використання AsyncTask оновіть подання від onPostExecute()методу
  2. Або виклик runOnUiThread()методу та оновлення подання всередині run()методу.

12

Ви можете перенести частину свого коду в інший потік, щоб вивантажити main threadта уникнути отримання ANR , NetworkOnMainThreadException , IllegalStateException (наприклад, Неможливо отримати доступ до бази даних на основній темі, оскільки це може потенційно заблокувати інтерфейс користувача протягом тривалого періоду часу).

Є кілька підходів, які вам слід вибрати, залежить від ситуації

Тема Java або Android HandlerThread

Нитки Java - це одноразове використання і відмирають після виконання методу її запуску.

HandlerThread - це зручний клас для запуску нової нитки, яка має петлю.

AsyncTask

AsyncTask розроблений для того, щоб бути допоміжним класом навколо Thread and Handler і не становить загальної рамки для нарізки. AsyncTasks в ідеалі слід використовувати для коротких операцій (максимум за кілька секунд.) Якщо вам потрібно тримати потоки протягом тривалих періодів часу, настійно рекомендується використовувати різні API, надані пакетом java.util.concurrent, наприклад кат , ThreadPoolExecutor і FutureTask .

Реалізація потокового пулу ThreadPoolExecutor , ScheduledThreadPoolExecutor ...

Клас ThreadPoolExecutor, який реалізує ExecutorService, що забезпечує тонкий контроль над потоком пулу (наприклад, розмір пулу ядра, максимальний розмір пулу, зберігайте живий час тощо)

ScheduledThreadPoolExecutor - клас, який розширює ThreadPoolExecutor. Він може планувати завдання після заданої затримки або періодично.

FutureTask

FutureTask виконує асинхронну обробку, однак, якщо результат ще не готовий або обробка не завершена, виклик get () буде блокувати нитку

AsyncTaskLoaders

AsyncTaskLoaders, оскільки вони вирішують багато проблем, притаманних AsyncTask

IntentService

Це вибір дефакто для тривалої обробки на Android, хорошим прикладом може бути завантаження чи завантаження великих файлів. Завантаження та завантаження може продовжуватися, навіть якщо користувач закриває додаток, і ви, звичайно, не хочете перешкоджати користувачеві користуватися додатком під час виконання цих завдань.

JobScheduler

Ефективно вам потрібно створити Сервіс і створити завдання за допомогою JobInfo.Builder, який визначає ваші критерії, коли запускати послугу.

RxJava

Бібліотека для складання асинхронних програм на основі подій з використанням спостережуваних послідовностей.

Корутини (Котлін)

Основна його суть, вона робить асинхронний код схожим на синхронний

Детальніше читайте тут , тут , тут , тут


Працював для мене ... Я активно використовував AsyncTask, але коли одне завдання працює, інше зачекає. Я хочу це вирішити. Зараз працюємо з Executeonexecutor. Подивимося, як це поведе себе на пристроях із низькою пам’яттю.
Suraj Shingade

Будь ласка, подивіться на метод: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, парами); одночасно запустити своє завдання
yoAlex5

10

Хоча вище є величезний пул рішень, ніхто не згадав com.koushikdutta.ion: https://github.com/koush/ion

Він також асинхронний і дуже простий у використанні:

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

10

Нові рішення Threadта рішення AsyncTask вже пояснені.

AsyncTaskв ідеалі слід використовувати для коротких операцій. Нормальне Threadне є кращим для Android.

Погляньте на альтернативне рішення за допомогою HandlerThread та Handler

HandlerThread

Зручний клас для початку нової нитки, яка має петлю. Потім петлю можна використовувати для створення класів обробників. Зверніть увагу, що start()все-таки потрібно дзвонити.

Обробник:

Обробник дозволяє вам надсилати та обробляти об'єкти повідомлення та запуску, пов'язані з повідомленнями. Кожен екземпляр обробника асоціюється з одним потоком і чергою повідомлення цього потоку. Коли ви створюєте новий обробник, він прив'язується до черги / черги повідомлень потоку, який його створює - з цього моменту він доставлятиме повідомлення та розгортання до цієї черги повідомлень та виконує їх, коли вони виходять із повідомлення чергу.

Рішення:

  1. Створіть HandlerThread

  2. виклик start()наHandlerThread

  3. Створіть Handler, отримуючи LooperвідHanlerThread

  4. Вставте код, пов'язаний з мережевою операцією, в Runnableоб’єкт

  5. Надішліть Runnableзавдання наHandler

Зразок фрагмента коду, адреса якого 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);

Плюси використання цього підходу:

  1. Створення нового Thread/AsyncTaskдля кожної операції в мережі є дорогим. Захист Thread/AsyncTaskбуде знищений та створений для наступних операцій у мережі. Але за допомогою Handlerта HandlerThreadпідходу ви можете подати багато мережевих операцій (як виконання завдань) до одиничних HandlerThread, використовуючи Handler.

8

RxAndroidє ще однією кращою альтернативою цій проблемі, і вона рятує нас від клопоту створювати теми, а потім публікувати результати на потоці Android UI. Нам просто потрібно вказати теми, над якими потрібно виконати завдання, і все обробляється всередині.

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. Вказавши (Schedulers.io()), RxAndroid запуститься getFavoriteMusicShows() на інший потік.

  2. За допомогою AndroidSchedulers.mainThread()ми хочемо спостерігати це спостережуване в потоці користувальницького інтерфейсу, тобто ми хочемо, щоб наш onNext()зворотний виклик викликався на потоці інтерфейсу користувача


8

Основним потоком є ​​потік інтерфейсу користувача, і ви не можете робити операцію в основному потоці, яка може блокувати взаємодію з користувачем. Вирішити це можна двома способами:

Змусити виконати завдання в основному потоці, як це

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

Або створіть простий обробник і оновіть основний потік, якщо хочете.

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

І для припинення використання потоку:

newHandler.removeCallbacks(runnable);

Для отримання додаткової інформації ознайомтесь із цим: Безболісна різьба


Дякую. Версія 1 допомагає додавати як першу дію в onCreate.
Інго

7

Це працює. Щойно зробила відповідь доктора Луіджі трохи простішою.

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

7

В Android операції з мережею не можна запускати в основному потоці. Ви можете використовувати Thread, AsyncTask (короткі завдання), Service (тривалі завдання) для виконання мережевих операцій.


7

Доступ до мережевих ресурсів з основного потоку (UI) викликає цей виняток. Використовуйте окремий потік або AsyncTask для доступу до мережевого ресурсу, щоб уникнути цієї проблеми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.