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

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

Код:

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

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

android.os.NetworkOnMainThreadException

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

Ответы [ 53 ]

16 голосов
/ 21 октября 2017

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

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

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)

Модернизация от Square

Это отличный выбор для API с несколькими конечными точками, позволяющий объявлять конечные точки ReST вместонеобходимость кодировать их индивидуально, как с другими библиотеками, такими как Ион или Волей.(веб-сайт: 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);
    }
}

Фрагмент фрагмента FinancesFragment

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.

Ion

Это отличная библиотека для этого типаaccess.

Пожалуйста, прочитайте отличный ответ msysmilu (https://stackoverflow.com/a/28559884/1024412)

Загрузка изображений по HTTP

Volley

Залп может также использоваться для 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)или конструктор (Диалог):

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/

15 голосов
/ 22 июля 2015

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

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

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

Решение:

  1. Вам необходимо создать новую тему
  2. или использовать класс AsyncTask

Путь:

Поместите все свои работы внутрь

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

Но:

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

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

Как?

  1. При использовании AsyncTask обновите представление из onPostExecute() метода
  2. или callrunOnUiThread() метод и представление обновления в методе run().
10 голосов
/ 17 февраля 2015

Хотя выше есть огромный пул решений, никто не упомянул 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
    }
});
9 голосов
/ 05 мая 2017

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

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

Посмотрите на альтернативное решение, используя HandlerThread и Обработчик

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.
9 голосов
/ 17 марта 2018

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

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

Java Тема или 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

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

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

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

Подробнее здесь:
8 способов выполнения асинхронной обработки в Android и подсчета
Эволюция доступа к сети Android
Использование пула потоков в Android
Управление потоками и пользовательскими службами

8 голосов
/ 09 июня 2017

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

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

7 голосов
/ 13 августа 2015

Доступ к сетевым ресурсам из основного потока (UI) вызывает это исключение. Чтобы избежать этой проблемы, используйте отдельный поток или AsyncTask для доступа к сетевому ресурсу.

7 голосов
/ 14 февраля 2015

Это работает.Просто сделал ответ доктора Луиджи немного проще.

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
7 голосов
/ 25 июня 2015

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

6 голосов
/ 10 июля 2017

Вы можете использовать KOTLIN и ANKO.

Kotlin - новый официальный язык для Android Подробнее об этом вы можете узнать здесь https://kotlinlang.org/docs/tutorials/kotlin-android.html

Anko поддерживаемая библиотекадля Kotlin в Android, некоторые документы здесь https://github.com/Kotlin/anko

Решение, которое действительно полезно и имеет всего несколько строк кода, написанного @AntonioLeiva https://antonioleiva.com/anko-background-kotlin-android/

doAsync {
    var result = runLongTask()
    uiThread {
        toast(result)
    }
}

Простое как оно есть, NetworkOnMainThread возникает, когда вы запускаете фоновое задание на UI Thread, поэтому вам нужно всего лишь запустить longTask job в фоновом режиме.Вы можете сделать это, используя этот метод и Kotlin с Anko в вашем приложении для Android.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...