Вопрос: Как исправить 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

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


1935


источник


Ответы:


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

2200



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

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

Добавить:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy); 

В вашем классе,

а также

ADD это разрешение в файле android manifest.xml:

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

Последствия:

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

У Android есть несколько хороших советов о хороших практиках программирования, которые нужно разработать для оперативности: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


542



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

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

342



You cannot perform network I/O on the UI thread on Honeycomb. Technically, it is possible on earlier versions of Android, but it is a really bad idea as it will cause your app to stop responding, and can result in the OS killing your app for being badly behaved. You'll need to run a background process or use AsyncTask to perform your network transaction on a background thread.

There is an article about Painless Threading on the Android developer site which is a good introduction to this, and it will provide you with a much better depth of an answer than can be realistically provided here.


122



The accepted answer has some significant down-sides. It is not advisable to use AsyncTask for networking unless you really know what you are doing. Some of the down-sides include:

  • AsyncTask's created as non-static inner classes have an implicit reference to the enclosing Activity object, its context, and the entire View hierarchy created by that activity. This reference prevents the Activity from being garbage collected until the AsyncTask's background work completes. If the user's connection is slow, and/or the download is large, these short-term memory leaks can become a problem - for example if the orientation changes several times (and you don't cancel the executing tasks), or the user navigates away from the Activity.
  • AsyncTask has different execution characteristics depending on the platform it executes on: prior to API level 4 AsyncTasks execute serially on a single background thread; from API level 4 through API level 10, AsyncTasks execute on a pool of up to 128 threads; from API level 11 onwards AsyncTask executes serially on a single background thread (unless you use the overloaded executeOnExecutor method and supply an alternative executor). Code that works fine when run serially on ICS may break when executed concurrently on Gingerbread, say, if you have inadvertent order-of-execution dependencies.

If you want to avoid short-term memory leaks, have well defined execution characteristics across all platforms, and have a base to build really robust network handling, you might want to consider:

  1. Using a library that does a nice job of this for you - there's a nice comparison of networking libs in this question, or
  2. Using a Service or IntentService instead, perhaps with a PendingIntent to return the result via the Activity's onActivityResult method.

IntentService approach

Down-sides:

  • More code and complexity than AsyncTask, though not as much as you might think
  • Will queue requests and run them on a single background thread. You can easily control this by replacing IntentService with an equivalent Service implementation, perhaps like this one.
  • Um, I can't think of any others right now actually

Up-sides:

  • Avoids the short-term memory leak problem
  • If your activity restarts while network operations are in-flight it can still receive the result of the download via its onActivityResult method
  • Better platform than AsyncTask to build and re-use robust networking code. Example: if you need to do an important upload, you could do it from AsyncTask in an Activity, but if the user context-switches out of the app to take a phone-call, the system may kill the app before the upload completes. It is less likely to kill an application with an active Service.
  • If you use your own concurrent version of IntentService (like the one I linked above) you can control the level of concurrency via the Executor.

Implementation summary

You can implement an IntentService to perform downloads on a single background thread quite easily.

Step 1: Create an IntentService to perform the download. You can tell it what to download via Intent extra's, and pass it a PendingIntent to use to return the result to the 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);
        }
    }
}

Step 2: Register the service in the manifest:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

Step 3: Invoke the service from the Activity, passing a PendingResult object which the Service will use to return the result:

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

Step 4: Handle the result in 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);
}

A github project containing a complete working Android-Studio/gradle project is available here.


115



  1. Do not use strictMode (only in debug mode)
  2. Do not change SDK version
  3. Do not use a separate thread

Use Service or AsyncTask

See also Stack Overflow question:

android.os.NetworkOnMainThreadException sending an email from Android


58



Do the network actions on another thread

For Example:

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

And add this to AndroidManifest.xml

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

50



You disable the strict mode using following code:

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

This is not recommended: use the AsyncTask interface.

Full code for both the methods


45



Network-based operations cannot be run on the main thread. You need to run all network-based tasks on a child thread or implement AsyncTask.

This is how you run a task in a child thread:

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

38