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"/>

...