Как я могу исправить 'android.os.NetworkOnMainThreadException'?

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

код:

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

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

android.os.NetworkOnMainThreadException

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

Ответ 1

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

Ответ 2

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

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

Добавить

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

Ответ 3

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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

Ответ 4

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

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

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

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

Подход IntentService

Вниз стороны:

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

Up-сторон:

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

Резюме реализации

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

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

Ответ 5

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

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

Ответ 7

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

Пример:

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

Использование Аннотации Android - это опция. Это позволит вам просто запустить любой метод в фоновом потоке:

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

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

Обратите внимание, что, хотя он обеспечивает преимущества простоты и удобочитаемости, у него есть свои недостатки.

Ответ 12

Это происходит в Android 3.0 и выше. Начиная с Android 3.0 и выше, они ограничили использование сетевых операций (функций, которые обращаются к Интернету) из потока в потоке основного потока/пользовательского интерфейса (что возникает из ваших методов create и on resume в действии).

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

Ответ 13

Ошибка связана с выполнением длительных операций в основном потоке. Вы можете легко устранить проблему, используя 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
    }
});

Ответ 14

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

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

Ответ 15

Верхний ответ spektom работает идеально.

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

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

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

Ответ 16

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

Ошибка является предупреждением SDK!

Ответ 17

Для меня это было так:

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

Устройство, на которое я тестировал свое приложение, было 4.1.2, которое представляет собой версию SDK версии 16!

Убедитесь, что целевая версия совпадает с вашей целевой целевой библиотекой Android. Если вы не знаете, что такое ваша целевая библиотека, щелкните правой кнопкой мыши ваш проект → Путь сборки → Android, и он должен быть отмечен.

Кроме того, как указывали другие, укажите правильные разрешения для доступа в Интернет:

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

Ответ 18

Просто для того, чтобы четко сказать что-то:

Основной поток - это в основном поток пользовательского интерфейса.

Поэтому, говоря, что вы не можете выполнять сетевые операции в основном потоке, вы не можете выполнять сетевые операции в потоке пользовательского интерфейса, а это значит, что вы не можете выполнять сетевые операции в блоке *runOnUiThread(new Runnable() { ... }* внутри какого-либо другого потока.

(У меня просто был длинный головокружающий момент, пытаясь понять, почему я получаю эту ошибку где-то, кроме моего основного потока. Вот почему, этот поток помог, и, надеюсь, этот комментарий поможет кому-то другому.)

Ответ 19

Используйте это в своей деятельности

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

Ответ 20

Это исключение возникает из-за любой тяжелой задачи, выполняемой в основном потоке, если эта выполняющая задача занимает слишком много времени.

Чтобы этого избежать, мы можем обрабатывать его с помощью потоков или исполнителей

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

Ответ 21

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

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

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 делает это проще (подробнее см. Этот удивительный ответ: fooobar.com/questions/23057/...).

Один доступ к API-интерфейсу ReST

Скажем, вы создаете приложение "погода в настроении", которое просматривает местоположение GPS-пользователей и проверяет текущую температуру в этой области и сообщает им настроение. Для этого типа приложения не требуется объявлять конечные точки API; ему просто нужно иметь доступ к одной конечной точке API.

ион

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

Пожалуйста, прочитайте msysmilu отличный ответ (fooobar.com/questions/206/...)

Загружать изображения через 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

Volley требует больше настроек, чем дооснащение. Вам нужно будет создать такой класс, чтобы настроить 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/

Ответ 22

Говоря простыми словами,

НЕ ДЕЛАЙТЕ СЕТЕВЫЕ РАБОТЫ В РЕЖИМЕ UI

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

Решение:

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

Путь:

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

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

Но:

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

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

Как?

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

Ответ 23

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

Ответ 24

Новые решения 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.

Ответ 25

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

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

Java Тема или Android HandlerThread

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

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

AsyncTask

AsyncTask разработан как вспомогательный класс для Thread и 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

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

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

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

Подробнее здесь, здесь, здесь, здесь

Ответ 26

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

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

Ответ 27

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

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

Ответ 28

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

Ответ 29

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

Ответ 30

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

Первый проверенный ответ - использование 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.