Câu hỏi Tôi làm cách nào để sửa android.os.NetworkOnMainThreadException?


Tôi gặp lỗi khi chạy dự án Android của mình cho RssReader.

Mã số:

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

Và nó cho thấy lỗi dưới đây:

android.os.NetworkOnMainThreadException

Làm cách nào để khắc phục sự cố này?


1982
2018-06-14 12:02


gốc


Đọc bài đăng trên blog này trên NetworkOnMainThreadException để biết thêm thông tin. Nó giải thích tại sao điều này xảy ra trên Android 3.0 trở lên. - Adrian Monk
Để được theo dõi nghi thức đầu tiên đọc về các yêu cầu mạng trong Android sau đó tôi muốn giới thiệu để nghiên cứu "Volley". - Anuj Sharma
Có nhiều thư viện thay thế giải quyết vấn đề này. Nhiều người được liệt kê ở cuối trang này. Nếu bạn có nhiều hơn, chúng tôi lấy chúng :) - Snicolas
Bạn cần chạy các hoạt động internet trên một sợi riêng biệt với luồng chính (UI) - Naveed Ahmad
stackoverflow.com/questions/16439587/…    ở trên liên kết là tốt - nguyenvangiangbn


Các câu trả lời:


Ngoại lệ này được ném khi một ứng dụng cố gắng thực hiện một hoạt động mạng trên luồng chính của nó. Chạy mã của bạn trong 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
    }
}

Cách thực hiện tác vụ:

Trong MainActivity.java bạn có thể thêm dòng này vào oncreate() phương pháp

new RetrieveFeedTask().execute(urlToRssFeed);

Đừng quên thêm điều này vào AndroidManifest.xml tập tin:

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

2240
2018-06-14 12:15



Tôi nghĩ đáng lưu ý ở đây là đoạn mã ở trên được cho là một lớp con (lớp bên trong), tốt nhất là riêng tư. Bằng cách đó khi AsyncTask kết thúc, bạn vẫn có thể thao tác các phần tử bên trong lớp của bạn. - dyslexicanaboko
Trên thực tế tôi đã làm điều tương tự như bạn đã đề cập ở trên nhưng m phải đối mặt với lỗi này java.lang.RuntimeException: Không thể tạo trình xử lý bên trong luồng mà không được gọi là Looper.prepare () - Dhruv Tyagi
Đây chính là câu trả lời sai. Tôi bắt gặp tất cả thời gian này trong mã người, và nó gây phiền nhiễu khi phải sửa chữa nó mọi lúc. AsyncTask không nên được sử dụng cho hoạt động mạng, vì nó được gắn với hoạt động, nhưng không được sử dụng trong vòng đời của hoạt động. Xoay thiết bị với tác vụ này đang chạy sẽ gây ra ngoại lệ và làm hỏng ứng dụng của bạn. Sử dụng một IntentService để giảm dữ liệu trong cơ sở dữ liệu sqlite thay thế. - Brill Pappin
Thận trọng, AsyncTask thường được sử dụng cho mỗi hoạt động mạng hoạt động khi nó không được. vòng đời của nó không đồng bộ với hoạt động. Để tìm nạp dữ liệu, bạn nên sử dụng IntentService và cơ sở dữ liệu đằng sau khung nhìn. - Brill Pappin
Tôi đề nghị AsyncTask Loaders cho những trường hợp như vậy. - Roman Gherta


Bạn hầu như luôn luôn nên chạy các hoạt động mạng trên một luồng hoặc như một tác vụ không đồng bộ.

Nhưng nó  có thể xóa giới hạn này và bạn ghi đè hành vi mặc định, nếu bạn sẵn sàng chấp nhận hậu quả.

Thêm vào:

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

StrictMode.setThreadPolicy(policy); 

Trong lớp của bạn,

THÊM quyền này trong tệp android manifest.xml:

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

Hậu quả:

Ứng dụng của bạn sẽ không phản hồi và bị khóa, người dùng cảm thấy chậm chạp và phải giết người, và bạn có nguy cơ người quản lý hoạt động giết ứng dụng của bạn và nói với người dùng rằng ứng dụng đã dừng.

Android có một số mẹo hay về các phương pháp lập trình tốt để thiết kế đáp ứng: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


557
2018-02-15 06:59



Đây là một ý kiến ​​tồi. giải pháp là để tránh mạng IO trên chủ đề chính (như các câu trả lời được chấp nhận cho thấy). - MByD
Với điều này bạn chỉ ẩn vấn đề thực sự của bạn. - Alex
@ AswistedUmbrella AsyncTask không thêm một trang mã, nó thêm 6 dòng (khai báo lớp, ghi chú ghi đè, doInBackground khai báo, 2 dấu ngoặc đóng và cuộc gọi đến execute()). Mặt khác, ngay cả một lần tìm nạp đơn lẻ từ một trang web như bạn đề cập sẽ mang lại độ trễ đáng kể trong phản hồi UI. Đừng lười biếng. - Zoltán
@TwistedUmbrella Đừng ngớ ngẩn. Đó là cách các ứng dụng xấu được tạo ra. Cho dù đó là tải xuống tệp văn bản hay thư viện hình ảnh, bạn nên thực hiện theo các phương pháp hay nhất. Và chạy mạng IO trên chủ đề chính không phải là một thực hành tốt nhất. - States
Được thăng hạng. Câu trả lời này là đúng, và đối với nhiều lập trình viên không ngây thơ hay ngu ngốc, nhưng đơn giản chỉ cần một SYNCHRONOUS (tức là: điều này phải chặn ứng dụng) gọi, đây là chính xác những gì cần thiết. Tôi thấy hạnh phúc hơn khi Android ném ngoại lệ theo mặc định (IMHO là một điều rất hữu ích để làm!) - nhưng tôi cũng không hài lòng khi nói "cảm ơn, nhưng - đây thực sự là những gì tôi dự định" và ghi đè nó. - Adam


Tôi đã giải quyết vấn đề này bằng cách sử dụng Thread.

Thread thread = new Thread(new Runnable() {

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

thread.start(); 

346
2018-01-21 16:31



Thay vì tạo một luồng mới mỗi khi bạn muốn thực hiện một hoạt động mạng, bạn cũng có thể sử dụng một dịch vụ thi hành luồng đơn. - Alex Lockwood
Đơn giản nhưng nguy hiểm. Runnable ẩn danh có tham chiếu ngầm định đến lớp kèm theo (ví dụ: Hoạt động hoặc Phân đoạn của bạn), ngăn không cho nó bị thu gom rác cho đến khi chuỗi hoàn tất. Bạn ít nhất nên đặt ưu tiên cho Process.BACKGROUND, nếu không luồng này sẽ chạy ở cùng mức độ ưu tiên với chủ đề chính / ui, cạnh tranh với các phương pháp vòng đời và tỷ lệ khung hình ui (xem cảnh báo trong nhật ký từ biên đạo múa). - Stevie
@Stevie làm thế nào để thiết lập các ưu tiên? cả runnble lẫn executorService đều không có phương thức setter như vậy - J. K.
@ J.K. Cung cấp ExecutorService của bạn với một ThreadFactory tùy chỉnh và gọi Thread.setPriority trên luồng trước khi trả về nó. - Stevie
Đặt mức độ ưu tiên cho mục đích thực hiện cuộc gọi mạng (sẽ chặn) có vẻ quá mức cần thiết. - john16384


Bạn không thể thực hiện mạng I / O trên chuỗi giao diện người dùng trên Honeycomb. Về mặt kỹ thuật, nó  có thể trên các phiên bản trước của Android, nhưng đó thực sự là một ý tưởng tồi vì nó sẽ khiến ứng dụng của bạn ngừng phản hồi và có thể khiến hệ điều hành giết ứng dụng của bạn vì bị hành vi xấu. Bạn sẽ cần chạy một tiến trình nền hoặc sử dụng AsyncTask để thực hiện giao dịch mạng của bạn trên một luồng nền.

Có một bài viết về Không đau Threading trên trang web dành cho nhà phát triển Android, đây là phần giới thiệu tốt về điều này và nó sẽ cung cấp cho bạn độ sâu tốt hơn cho câu trả lời có thể được cung cấp thực tế ở đây.


124
2018-06-14 12:07





Câu trả lời được chấp nhận có một số mặt giảm đáng kể. Không nên sử dụng AsyncTask cho mạng trừ khi bạn có thật không biết bạn đang làm gì. Một số mặt bên dưới bao gồm:

  • AsyncTask được tạo ra như là các lớp bên trong không tĩnh có một tham chiếu ngầm đến đối tượng Activity kèm theo, ngữ cảnh của nó và toàn bộ hệ thống phân cấp View được tạo ra bởi hoạt động đó. Tham chiếu này ngăn không cho Hoạt động bị thu gom rác cho đến khi công việc nền của AsyncTask hoàn thành. Nếu kết nối của người dùng chậm và / hoặc tải xuống lớn, những rò rỉ bộ nhớ ngắn hạn này có thể trở thành vấn đề - ví dụ: nếu định hướng thay đổi nhiều lần (và bạn không hủy tác vụ thực thi) hoặc người dùng điều hướng cách xa Hoạt động.
  • AsyncTask có các đặc tính thực thi khác nhau tùy thuộc vào nền tảng mà nó thực hiện trên: trước khi cấp API 4 AsyncTask thực thi serially trên một luồng nền đơn; từ API cấp 4 đến cấp API 10, AsyncTask thực thi trên một nhóm gồm tối đa 128 chủ đề; từ cấp API 11 trở đi AsyncTask thực thi serially trên một luồng nền đơn (trừ khi bạn sử dụng quá tải executeOnExecutorvà cung cấp một nhà điều hành thay thế). Mã hoạt động tốt khi chạy serially trên ICS có thể bị ngắt khi được thực hiện đồng thời trên Gingerbread, giả sử, nếu bạn có các phụ thuộc không thực thi lệnh vô ý.

Nếu bạn muốn tránh rò rỉ bộ nhớ ngắn hạn, có các đặc tính thực thi được xác định rõ ràng trên tất cả các nền tảng và có cơ sở để xây dựng xử lý mạng thực sự mạnh mẽ, bạn có thể muốn xem xét:

  1. Sử dụng thư viện thực hiện công việc tốt đẹp này cho bạn - có sự so sánh tốt đẹp về libs mạng trong câu hỏi này, hoặc là
  2. Sử dụng một Service hoặc là IntentService thay vào đó, có lẽ với một PendingIntent để trả lại kết quả thông qua Hoạt động onActivityResult phương pháp.

Phương pháp IntentService

Down-side:

  • Mã và độ phức tạp hơn AsyncTask, mặc dù không nhiều như bạn nghĩ
  • Sẽ xếp hàng các yêu cầu và chạy chúng trên một Độc thân chủ đề nền. Bạn có thể dễ dàng kiểm soát điều này bằng cách thay thế IntentService với một tương đương Service thực hiện, có lẽ như cái này.
  • Um, tôi không thể nghĩ ra bất kỳ người nào khác ngay bây giờ

Up-side:

  • Tránh vấn đề rò rỉ bộ nhớ ngắn hạn
  • Nếu hoạt động của bạn khởi động lại trong khi các hoạt động mạng đang hoạt động thì nó vẫn có thể nhận được kết quả của quá trình tải xuống thông qua onActivityResult phương pháp
  • Nền tảng tốt hơn so với AsyncTask để xây dựng và tái sử dụng mã mạng mạnh mẽ. Ví dụ: nếu bạn cần tải lên quan trọng, bạn có thể làm điều đó từ AsyncTask trong một Activity, nhưng nếu bối cảnh người dùng chuyển đổi khỏi ứng dụng để thực hiện cuộc gọi điện thoại, hệ thống có thể giết ứng dụng trước khi quá trình tải lên hoàn tất. Nó là ít có khả năng để giết một ứng dụng với một hoạt động Service.
  • Nếu bạn sử dụng phiên bản đồng thời của riêng bạn IntentService (giống như cái tôi đã liên kết ở trên), bạn có thể kiểm soát mức độ đồng thời thông qua Executor.

Tóm tắt triển khai

Bạn có thể thực hiện một IntentService để thực hiện tải xuống trên một chuỗi nền đơn khá dễ dàng.

Bước 1: Tạo một IntentService để thực hiện tải xuống. Bạn có thể cho biết những gì cần tải xuống qua Intent thêm, và vượt qua nó PendingIntent để sử dụng để trả về kết quả cho 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);
        }
    }
}

Bước 2: Đăng ký dịch vụ trong tệp kê khai:

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

Bước 3: Gọi dịch vụ từ Activity, truyền đối tượng PendingResult mà Dịch vụ sẽ sử dụng để trả về kết quả:

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

Bước 4: Xử lý kết quả trong 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);
}

Một dự án github chứa một dự án Android-Studio / gradle hoàn chỉnh có sẵn đây.


118
2018-01-22 13:17



IntentService là cách chính xác để thực hiện điều này, không nhổ tận gốc vì AsyncTask chính xác là cách không thực hiện. - Brill Pappin
@BrillPappin Tôi gần như hoàn toàn đồng ý và đã viết lại để nhấn mạnh những hạn chế của AsyncTask. (Tôi vẫn nghĩ rằng có một rất nhỏ số trường hợp - nếu bạn thực sự biết mình đang làm gì - nó có thể được phép sử dụng AsyncTask, nhưng câu trả lời được chấp nhận không chỉ ra bất kỳ hạn chế nào và là cách quá phổ biến cho những điều tốt đẹp của Android). - Stevie
Bạn có thực sự cần IllustrativeRSS? Điều gì sẽ xảy ra nếu bạn không làm việc với các công cụ RSS? - Cullub


  1. Không sử dụng strictMode (chỉ trong chế độ gỡ lỗi)
  2. Không thay đổi phiên bản SDK
  3. Không sử dụng một sợi riêng biệt

Sử dụng Dịch vụ hoặc AsyncTask

Xem thêm Câu hỏi tràn ngăn xếp:

android.os.NetworkOnMainThreadException gửi email từ Android


59
2017-08-18 09:18



Có lẽ đáng nhấn mạnh rằng nếu bạn sử dụng một Dịch vụ, bạn sẽ vẫn cần tạo một chuỗi riêng biệt - Các cuộc gọi lại dịch vụ chạy trên chuỗi chính. Mặt khác, IntentService chạy phương thức onHandleIntent của nó trên một luồng nền. - Stevie
bạn không nên sử dụng AsyncTask cho các hoạt động chạy dài! Nguyên tắc chỉ định tối đa 2 đến 3 giây - Dage


Thực hiện tác vụ mạng trên một chuỗi khác

Ví dụ:

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

Và thêm vào AndroidManifest.xml

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

51
2017-12-24 14:33



Nhưng làm thế nào chúng ta có thể tìm ra khi nào thread kết thúc trong việc này để chúng ta có thể thực hiện các công việc tiếp theo trong thread UI? AsyncTask cung cấp cơ sở để thực hiện điều đó. Có cách nào để làm tương tự bằng cách sử dụng các chủ đề runnable? - Piyush Soni
Nó sẽ xử lý mã của bạn từng bước, do đó, ở cuối mã kết thúc, bạn cần sử dụng trình xử lý quay lại luồng UI - henry4343
mobileorchard.com/… - henry4343
Bạn có thể sử dụng tác vụ không đồng bộ hoặc dịch vụ ý định, vì nó thực thi trên luồng công nhân. - Chetan Chaudhari


Bạn tắt chế độ nghiêm ngặt bằng cách sử dụng mã sau:

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

Điều này không được khuyến nghị: sử dụng AsyncTaskgiao diện.

Toàn mã cho cả hai phương thức


46
2017-10-24 07:10



Có lỗi ANR sẽ đến. nghĩa là Ứng dụng không phản hồi sau 5 giây. - Muhammad Mubashir
Đây là một câu trả lời thực sự xấu. Bạn không nên thay đổi chính sách của thread nhưng để viết mã tốt hơn: không thực hiện các hoạt động mạng trên luồng chính! - shkschneider
Không tốt, nhưng hữu ích cho POC / Debugging - hB0
@Sandeep Bạn và những người xem khác cũng nên đọc nội dung này. stackoverflow.com/a/18335031/3470479 - Prakhar1001


Các hoạt động dựa trên mạng không thể chạy trên luồng chính. Bạn cần chạy tất cả các tác vụ dựa trên mạng trên một chuỗi con hoặc triển khai AsyncTask.

Đây là cách bạn chạy một nhiệm vụ trong một chủ đề con:

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

39
2018-01-14 06:14



Vô danh Runnable là không phải là cách tốt nhất, vì nó có một tham chiếu ngầm đến lớp bao quanh và ngăn chặn nó từ GC ed cho đến khi thread hoàn tất! Chủ đề này cũng sẽ chạy ở cùng mức độ ưu tiên với chủ đề chính / US, cạnh tranh với các phương pháp vòng đời và tỷ lệ khung hình giao diện người dùng! - Yousha Aleayoub


Đặt mã của bạn bên trong:

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

Hoặc là:

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

37
2017-07-28 10:43



Thứ hai sẽ là tốt nhất so với đầu tiên cho api trên 11 - Rohit Goswami
đọc nhận xét dưới: stackoverflow.com/a/21107128/1429432 - Yousha Aleayoub
Async hoạt động trên hầu hết các trường hợp - Ajay Pandya


Sử dụng Chú thích Android là một lựa chọn. Nó sẽ cho phép bạn chỉ cần chạy bất kỳ phương thức nào trong một luồng nền:

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

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

Lưu ý rằng mặc dù nó cung cấp các lợi ích của sự đơn giản và dễ đọc, nó có những nhược điểm của nó.


36
2018-06-12 03:10



những bất lợi là gì? - Gavriel
@Gavriel nó tạo ra bản sao của tất cả mọi thứ bạn chú thích, cho dù đó là một phương pháp, hoạt động, phân đoạn, singleton vv, do đó, có nhiều gấp đôi mã và phải mất nhiều thời gian để biên dịch nó. Nó cũng có thể có một số vấn đề do lỗi trong thư viện. Gỡ lỗi và tìm lỗi sẽ trở nên khó khăn hơn. - Oleksiy