networkonmainthread - android.os.NetworkOnMainThreadException

  显示原文与译文双语对照的内容

在下面的代碼中,我在運行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

我該如何解決此問題?

时间:

當應用程序試圖在它的主線程上執行聯網操作時,拋出這裡異常。 在 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;
 }
 }

 protected void onPostExecute(RSSFeed feed) {
//TODO: check this.exception 
//TODO: do something with the feed
 }
}

如何執行任務: 在 MainActivity.java 文件中,你可以在 oncreate() 方法中添加此行


new RetrieveFeedTask().execute(urlToRssFeed);

別忘了將這個添加到 AndroidManifest.xml file:


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

你應該總是執行接受的應答建議,但是如果你真的知道更好的,並且必須同時進行它,那麼你可以重寫默認行為,如下所示。

添加:


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

StrictMode.setThreadPolicy(policy); 

在你的課上

在 android manifest.xml file: 中添加這裡許可權


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

我用一個新的線程解決這個問題


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

thread.start(); 

無法執行網路i/o 上的用戶界麵線程上Honeycomb 。 于被嚴重behaved,相關的技術上,它 可能對更早版本的Android,不過這是個很糟糕的主意,因為它會導致你的應用程序停止響應,並且可能會導致操作系統殺死 app 。 你需要運行後台進程或者使用AsyncTask在後台線程上執行你的網路事務。

上有一篇關於無痛線程安卓開發者社交網站,它很好地介紹了這裡,並且它將為你提供一個更好的深度的答案比可以在這裡提供真實地。

  1. 不使用 strictMode ( 僅在調試模式下)
  2. 不更改SDK版本
  3. 不使用單獨的線程

使用服務或者最終用戶

另請參閱

android.os.NetworkOnMainThreadException 從Android伺服器發送電子郵件

可以接受的答案有幾個,每個都有不同的權衡。 讓我開始說,接受的答案是好的,我將它 up-voted,但它不是唯一的方法,它有一些 down-sides:

  • 創建asynctask作為non-static內部類具有對封閉 Activity 對象,它的上下文和由該 Activity 創建的整個視圖層次結構的隱式引用。 這裡引用防止 Activity 被垃圾回收,直到臨時任務的背景完成。 如果用戶的連接速度較慢或者/或者下載很大,則這些短期內存泄漏會成為問題- 例如如果方向更改了幾次( 並且不取消正在執行的任務),或者用戶離開 Activity 。
  • AsyncTask有不同的執行特性,具體取決於它執行的平台: 在API級別 4之前,在一個後台線程上串列執行 AsyncTasks ;從API級別 4到API級別 10 ;AsyncTasks在最多 128個線程上執行,在一個後台線程( 除非使用重載的executeOnExecutor 方法並提供一個替代執行器) 上執行。 ic上的代碼時都可以正常工作的運行以每秒個併發執行時可能會中斷在姜,說,如果你有無意order-of-execution依賴項。

如果你想避免短期內存泄漏,那麼在所有平台上都有良好的執行特性,並且有一個構建真正健壯的網路處理的基礎,你可能需要考慮:

  1. 這個問題,or,使用聯網的一個庫,它幫你勝任這個這個- 有一個很好的比較 libs
  2. 只需通過 Service 或者 IntentService 而不是,或許就用 PendingIntent 來返回結果的onActivityResult Activity 方法。

IntentService方法

Down-sides:

  • AsyncTask 更多的代碼和複雜性,儘管不如你想象的那樣多
  • 將對請求進行排隊並將它的運行在一個的後台線程上。 你可以通過將 IntentService 替換為等價的Service 實現來輕鬆控制它,也許像這樣的。
  • 呃我現在想不出其他的東西

Up-sides:

  • 避免短期內存泄漏問題
  • 如果你的Activity 在網路操作為in-flight時重新啟動,它仍然可以通過 onActivityResult 方法接收下載結果
  • 比AsyncTask更好的平台構建和re-use健壯的網路代碼。 例如如果你需要做一個重要的上傳,你一定能做到從 AsyncTask 在某一 Activity 。但是如果用戶context-switches不受該應用需要很 phone-call,則系統可能殺死該應用在上載完成之前。 憑借有效的這是不太可能殺死運行處理速度
  • 如果你使用自己的IntentService ( 就像我上面鏈接的那個)的併發版本,你可以通過 Executor 控制併發級別。

實現摘要

你可以實現 IntentService 來在單個後台線程上執行下載。

步驟 1: 創建一個 IntentService 來執行下載。 你可以告訴它通過 Intent 下載的內容,並通過一個 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);
}

一個包含完整工作 android-studio/gradle項目的github項目在這裡是

在 Android 3.0和更高版本中發生這種情況。 從 Android 3.0和上版本,他們限制了使用網路操作( 訪問互聯網的函數) 在主線程/ui線程( 在 Activity 中的創建和恢復方法中產生什麼) 中運行。

這是為了鼓勵使用單獨的線程進行網路操作。 有關如何執行網路活動的更多詳細信息,請參閱兼職

你可以使用以下代碼禁用嚴格模式:


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

這是不推薦的。 使用AsyncTask介面。

兩種方法的完整代碼: http://www.javaexperience.com/android-create-non-blocking-user-interface/

無法在主線程上運行基於網路的操作。 你需要在一個子線程上運行所有的基於網路的任務或者實現 AsyncTask 。

在子線程中運行詢問:


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

在其他線程上執行網路操作


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

並將它的添加到 AndroidManifest.xml


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

...