Как исправить 'android.os.NetworkOnMainThreadException'?

2396

Я получил ошибку при запуске моего проекта 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
Вам нужно запускать действия в Интернете отдельно от основного потока (пользовательского интерфейса)
Naveed Ahmad
«Из-за ошибки в предыдущих версиях Android система не помечала запись в TCP-сокет в основном потоке как нарушение строгого режима. Android 7.0 исправляет эту ошибку. Приложения, демонстрирующие такое поведение, теперь выдают android.os. NetworkOnMainThreadException «. - Значит, некоторые из нас не сталкивались с этим до недавнего времени! developer.android.com/about/versions/nougat/…
Jay

Ответы:

2548

Это исключение выдается, когда приложение пытается выполнить сетевую операцию в своем основном потоке. Запустите ваш код в 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 заканчивается, вы все равно можете манипулировать внутренностями вашего класса.
Дислексиканабоко
4
На самом деле я сделал то же самое, что вы упомянули выше, но столкнулся с этой ошибкой java.lang.RuntimeException: не могу создать обработчик внутри потока, который не вызвал Looper.prepare ()
Dhruv Tyagi
68
Это совершенно неправильный ответ. Я постоянно сталкиваюсь с этим в народном кодексе, и раздражает необходимость постоянно его исправлять. AsyncTask не следует использовать для сетевой активности, поскольку она связана с активностью, но не с жизненным циклом активности. Вращение устройства с этой задачей вызовет исключение и приведет к сбою приложения. Вместо этого используйте IntentService, который отбрасывает данные в базу данных sqlite.
Брилл Паппин
5
Внимание, AsyncTask часто используется для сетевых операций за активность, когда это не должно быть. его жизненный цикл не синхронизирован с деятельностью. Для извлечения данных вы должны использовать IntentService и базу данных за представлением.
Брилл Паппин
1
@BrillPappin, FWIW, это Android Руководство Разработчик использует AsyncTask, и заботится об изменении конфигурации.
HeyJude
678

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

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

Добавить:

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.

user1169115
источник
456
Это очень плохая идея. решение состоит в том, чтобы избежать сетевого ввода-вывода в главном потоке (как показывает принятый ответ).
MByD
74
При этом вы скрываете только свою реальную проблему.
Алекс
28
@TwistedUmbrella AsyncTask не добавляет страницу кода, она добавляет 6 строк (объявление класса, аннотацию переопределения, doInBackgroundобъявление, 2 закрывающих скобки и вызов execute()). С другой стороны, даже одна выборка с сайта, о которой вы упомянули, приводит к значительному отставанию скорости отклика пользовательского интерфейса. Не ленись.
Золтан
19
Я думаю, что это идеальное решение, если вы хотите просто запустить пример кода, чтобы увидеть, работает ли что-то перед реализацией правильной AsyncTask. Вот почему я проголосовал за этот ответ, хотя, как уже говорили все остальные, это не должно быть сделано в производственном приложении, только в качестве быстрого обходного пути для теста dev.
hooked82
95
Upvoted. Этот ответ является правильным, и для многих программистов, которые не являются ни наивными, ни глупыми, но которые просто требуют вызова SYNCHRONOUS (то есть: это должно блокировать приложение ), это именно то, что нужно. Я более чем счастлив, что Android генерирует исключение по умолчанию (ИМХО, это очень «полезная» вещь!) - но я также счастлив сказать «спасибо, но - это на самом деле то, что я намеревался» и переопределить Это.
Адам
428

Я решил эту проблему с помощью нового 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
источник
7
Вместо того, чтобы создавать новый поток каждый раз, когда вы хотите выполнить сетевую операцию, вы также можете использовать службу исполнения одного потока.
Алекс Локвуд
66
Просто, но опасно. Анонимный Runnable имеет неявную ссылку на включающий класс (например, ваш Activity или Fragment), предотвращая его сборку мусора до завершения потока. Вы должны по крайней мере установить приоритет Process.BACKGROUND, иначе этот поток будет работать с тем же приоритетом, что и основной поток / поток пользовательского интерфейса, конкурируя с методами жизненного цикла и частотой кадров пользовательского интерфейса (следите за предупреждениями в журнале от хореографа).
Стиви
1
@ Stevie, как установить приоритет? ни runnble, ни executorService не имеют такого метода установки
JK
1
@JK Предоставьте вашему ExecutorService пользовательский ThreadFactory и вызовите Thread.setPriority в потоке перед его возвратом.
Стиви
1
«Использование потоков непосредственно в Android не рекомендуется. Это вызывает больше проблем, чем решает». Хотите уточнить это? На самом деле AsyncTask считается устаревшим именно для этого ... techyourchance.com/asynctask-deprecated
Fran Marzoa
170

Принятый ответ имеет несколько существенных недостатков. Не рекомендуется использовать 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 подход

Down-сторона:

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

Вверх-стороны:

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

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

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

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

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 плохое управление памятью, а не программисты.
Саид Халафинеджад
Я просто скажу: этот ответ кажется скорее способом поделиться проектом, чем лучшей альтернативой AsyncTask. Ошибка должна была помешать разработчикам отстать от пользовательского интерфейса, не обязательно побуждая их рисковать безопасностью с помощью множества намерений / сервисов.
заброшенная корзина
144

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

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

Марк Эллисон
источник
76
  1. Не используйте строгий режим (только в режиме отладки)
  2. Не меняйте версию SDK
  3. Не используйте отдельный поток

Использовать сервис или AsyncTask

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

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

venergiac
источник
8
Возможно, стоит подчеркнуть, что если вы используете Сервис, вам все равно нужно будет создать отдельный поток - обратные вызовы Сервиса выполняются в основном потоке. IntentService, с другой стороны, запускает свой метод onHandleIntent в фоновом потоке.
Стиви
Вы не должны использовать AsyncTask для длительных операций! Руководящие указания указывают от 2 до 3 секунд макс.
Dage
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"/>
henry4343
источник
5
Но как мы можем узнать, когда поток завершится в этом, чтобы мы могли выполнить следующий набор задач в потоке пользовательского интерфейса? AsyncTask предоставляет возможность сделать это. Есть ли способ сделать то же самое с помощью запускаемых потоков?
Пиюш Сони
3
Он будет обрабатывать ваш код шаг за шагом, поэтому в конце кода вы должны использовать обработчик обратно в поток пользовательского интерфейса
henry4343
1
Вы можете использовать асинхронную задачу или службу намерений, потому что она выполняется в рабочем потоке.
Четан Чаудхари
63

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

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

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

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

Sandeep
источник
2
Да, ANR ошибка будет. означает, что приложение не отвечает в течение 5 секунд.
Мухаммед Мубашир,
12
Это действительно плохой ответ. Вы не должны изменять политику потока, а писать лучший код: не выполняйте сетевые операции в основном потоке!
Шкшнайдер
@Sandeep Вы и другие зрители тоже должны это прочитать. stackoverflow.com/a/18335031/3470479
Prakhar1001
53

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

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

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();
Дхрув Джиндал
источник
1
Anonymous Runnable - НЕ лучший способ, так как он имеет неявную ссылку на включающий класс и не позволяет ему редактироваться GC до завершения потока! Также этот поток будет работать с тем же приоритетом, что и основной поток / поток США, конкурируя с методами жизненного цикла и частотой кадров пользовательского интерфейса!
Юша Алеауб
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
@ Гавриил создает дубликаты всего, что вы аннотируете, будь то метод, действие, фрагмент, синглтон и т. Д., Поэтому кода в два раза больше, и его компиляция занимает больше времени. Это может также иметь некоторые проблемы из-за ошибок в библиотеке. Отладка и поиск ошибок станут более сложными.
Алексей
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

Вы не должны выполнять никаких трудоемких задач в главном потоке (потоке пользовательского интерфейса), таких как сетевые операции, операции ввода-вывода файлов или операции с базами данных SQLite. Таким образом, для такого рода операций вы должны создать рабочий поток, но проблема в том, что вы не можете напрямую выполнять какие-либо операции, связанные с пользовательским интерфейсом, из вашего рабочего потока. Для этого вы должны использовать Handlerи передать Message.

Чтобы упростить все эти вещи, Android предоставляет различные способы, такие как AsyncTask , AsyncTaskLoader, CursorLoaderили IntentService. Таким образом, вы можете использовать любой из них в соответствии с вашими требованиями.

Капил Ватс
источник
40

Верхний ответ спектом работает отлично.

Если вы пишете AsyncTaskinline, а не расширяете как класс, и, кроме того, если есть необходимость получить ответ AsyncTask, можно использовать get()метод, как показано ниже.

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

(Из его примера.)

sivag1
источник
5
использование get()- плохая идея ... это делает AsyncTask снова "синхронизированным"
Selvin
Есть ли лучший выход из этого? @Selvin
sivag1
2
Я думаю, что вы могли бы сообщить основной ветке о результате. Например, отправить широковещательную рассылку в основную ветку, включая результат.
Чайн Гари,
32

Это выбрасывается только для приложений, ориентированных на Honeycomb SDK или выше. Приложениям, нацеленным на более ранние версии SDK, разрешено создавать сети в своих основных потоках цикла событий.

Ошибка - предупреждение SDK!

грушевый сидр
источник
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"/>
rharvey
источник
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.
    }
});
Amardeep
источник
18

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

Я расскажу о нескольких случаях использования для выполнения сетевых операций и одном или двух решениях для каждого.

ReST через HTTP

Как правило, Json, может быть XML или что-то еще

Полный доступ к API

Допустим, вы пишете приложение, которое позволяет пользователям отслеживать цены на акции, процентные ставки и курсы валют. Вы найдете Json API, который выглядит примерно так:

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

FinancesApiBuilder

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 from Square, как указано выше ( http://square.github.io/retrofit/ )

Допустим, вы создаете приложение для социальных сетей и хотите загрузить фотографии друзей.

build.gradle

Добавьте эту строку на уровень вашего модуля buid.gradle:

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

ImageFetch.java

Залп требует больше настроек, чем модификация. Вам нужно будет создать такой класс, чтобы настроить 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 (Fragment, Activity) или конструктор (Dialog):

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/

KG6ZVP
источник
16

Проще говоря,

НЕ СЕТЬ РАБОТАЙТЕ В РЕЗЬБЕ UI

Например, если вы делаете HTTP-запрос, это действие сети.

Решение:

  1. Вы должны создать новую тему
  2. Или используйте класс AsyncTask

Путь:

Положите все свои работы внутри

  1. run() метод новой темы
  2. Или doInBackground() метод класса AsyncTask.

Но:

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

Если вы этого не сделаете, вы получите ViewRootImpl$CalledFromWrongThreadException.

Как?

  1. При использовании AsyncTask обновите представление из onPostExecute()метода
  2. Или вызовите runOnUiThread()метод и обновите представление внутри run()метода.
Nabin
источник
12

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

Есть несколько подходов, которые вы должны выбрать, зависит от ситуации

Java Thread или Android HandlerThread

Потоки Java используются только один раз и умирают после выполнения метода run.

HandlerThread - это удобный класс для запуска нового потока, в котором есть петлитель.

AsyncTask

AsyncTask разработан как вспомогательный класс для Thread и Handler и не представляет собой универсальную среду потоков. AsyncTasks в идеале следует использовать для коротких операций (максимум несколько секунд). Если вам нужно, чтобы потоки работали в течение длительного времени, настоятельно рекомендуется использовать различные API, предоставляемые пакетом java.util.concurrent, такие как Executor , ThreadPoolExecutor и FutureTask .

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

Класс ThreadPoolExecutor, который реализует ExecutorService, который дает точный контроль над пулом потоков (например, размер пула ядра, максимальный размер пула, время поддержки и т. Д.)

ScheduledThreadPoolExecutor - класс, который расширяет ThreadPoolExecutor. Он может планировать задачи после определенной задержки или периодически.

FutureTask

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

AsyncTaskLoaders

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

IntentService

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

JobScheduler

По сути, вам нужно создать Службу и создать задание, используя JobInfo.Builder, в котором указаны ваши критерии, когда следует запускать Службу.

RxJava

Библиотека для составления асинхронных и событийных программ с использованием наблюдаемых последовательностей.

Сопрограммы (Котлин)

Основная суть в том, что асинхронный код выглядит так же, как синхронный

Узнайте больше здесь , здесь , здесь , здесь

yoAlex5
источник
Работал для меня ... Я широко использовал AsyncTask, но когда одна задача выполняется, другая будет ждать. Я хочу решить это. Сейчас работаю с executeonexecutor. Давайте посмотрим, как это будет вести себя на устройствах с низкой памятью.
Сурадж Шингейд
Пожалуйста, взгляните на метод: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); выполнить свою задачу одновременно
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
    }
});
msysmilu
источник
10

Новые решения Threadи решения AsyncTask уже были объяснены.

AsyncTaskв идеале следует использовать для коротких операций. Обычный Threadне предпочтителен для Android.

Взгляните на альтернативное решение, используя HandlerThread и Handler

HandlerThread

Удобный класс для запуска нового потока, который имеет петлитель. Цикл затем может быть использован для создания классов обработчиков. Обратите внимание, что start()все еще должен быть вызван.

Обработчик:

Обработчик позволяет отправлять и обрабатывать объекты Message и Runnable, связанные с MessageQueue потока. Каждый экземпляр обработчика связан с одним потоком и очередью сообщений этого потока. Когда вы создаете новый обработчик, он привязывается к потоку / очереди сообщений потока, который его создает - с этого момента он будет доставлять сообщения и исполняемые файлы в эту очередь сообщений и выполнять их по мере их выхода из сообщения. очередь.

Решение:

  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

Есть еще один очень удобный способ решения этой проблемы - использовать возможности параллелизма rxJava. Вы можете выполнять любую задачу в фоновом режиме и очень удобно размещать результаты в главном потоке, поэтому эти результаты будут переданы в цепочку обработки.

Первый проверенный ответ - использовать AsynTask. Да, это решение, но в настоящее время оно устарело, потому что вокруг появились новые инструменты.

String getUrl() {
    return "SomeUrl";
}

private Object makeCallParseResponse(String url) {
    return null;
    //
}

private void processResponse(Object o) {

}

Метод getUrl предоставляет URL-адрес, и он будет выполняться в основном потоке.

makeCallParseResponse (..) - выполняет реальную работу

processResponse (..) - обработает результат в основном потоке.

Код для асинхронного выполнения будет выглядеть так:

rx.Observable.defer(new Func0<rx.Observable<String>>() {
    @Override
    public rx.Observable<String> call() {
        return rx.Observable.just(getUrl());
    }
})
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.io())
    .map(new Func1<String, Object>() {
        @Override
        public Object call(final String s) {
            return makeCallParseResponse(s);
        }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
             processResponse(o);
        }
    },
    new Action1<Throwable>() {
        @Override
        public void call(Throwable throwable) {
            // Process error here, it will be posted on
            // the main thread
        }
    });

По сравнению с AsyncTask этот метод позволяет переключать планировщики произвольное количество раз (скажем, извлекать данные из одного планировщика и обрабатывать эти данные из другого (скажем, Scheduler.computation ()). Вы также можете определить свои собственные планировщики.

Чтобы использовать эту библиотеку, включите в файл build.gradle следующие строки:

   compile 'io.reactivex:rxjava:1.1.5'
   compile 'io.reactivex:rxandroid:1.2.0'

Последняя зависимость включает поддержку планировщика .mainThread ().

Есть отличная книга для rx-java .

Алекс Шутов
источник
довольно обширный подход, это было бы запланировано нашим собственным, и я не знаю, почему мы должны, если это уже вариант на стороне Android?
Prakhar1001
8

RxAndroidЭто еще одна лучшая альтернатива этой проблеме, которая избавляет нас от хлопот, связанных с созданием потоков и публикацией результатов в потоке пользовательского интерфейса Android. Нам просто нужно указать потоки, в которых должны выполняться задачи, и все обрабатывается внутренне.

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. По specifiying (Schedulers.io()), RxAndroid будет работать getFavoriteMusicShows() на другом потоке.

  2. Используя это, AndroidSchedulers.mainThread()мы хотим наблюдать это Observable в потоке пользовательского интерфейса, т.е. мы хотим, чтобы наш 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();
Kacy
источник
7

На Android сетевые операции не могут выполняться в главном потоке. Вы можете использовать Thread, AsyncTask (краткосрочные задачи), Service (долгосрочные задачи) для выполнения сетевых операций.

Понсуямбу Велладурай
источник