我如何解决android.os.NetworkOnMainThreadException?

我运行RssReader的Android项目时出错。

码:

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 

我该如何解决这个问题?

当应用程序尝试在其主线程上执行联网操作时,会引发此exception。 在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文件:

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

您应该几乎总是在一个线程上运行networking操作,或者作为一个asynchronous任务。 但是如果你知道的更好,并愿意接受后果,并且必须在主线程上进行networking操作,则可以覆盖默认行为:

加:

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

在你的class上,

在android manifest.xml文件中添加这个权限:

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

后果:

你的应用程序将会(在networking连接不正常的地方)变得没有响应和locking,用户感觉到速度缓慢,并且必须进行一次强制杀手,你冒着活动pipe理器杀死你的应用程序,并告诉用户应用程序已经停止。

Android有一些很好的编程实践的技巧来devise响应: http : //developer.android.com/reference/android/os/NetworkOnMainThreadException.html

我用一个新的Thread解决了这个问题。

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

您不能在Honeycomb上的UI线程上执行networkingI / O。 从技术上讲,它可能在早期版本的Android上,但它是一个非常糟糕的主意,因为它会导致您的应用程序停止响应,并可能导致操作系统因为行为不端而导致您的应用程序被终止。 您需要运行后台进程或使用AsyncTask在后台线程上执行networking事务。

在Android开发人员网站上有一篇关于无痛线程的文章,这是一个很好的介绍,它将为您提供比现实中提供的更好的深度答案。

接受的答案有一些显着的不利因素。 除非你真的知道你在做什么,否则不build议使用AsyncTask进行联网。 一些不利的方面包括:

  • 创build为非静态内部类的AsyncTask对封闭的Activity对象,其上下文以及由该活动创build的整个View层次结构具有隐式引用。 这个引用可以防止Activity被垃圾收集,直到AsyncTask的后台工作完成。 如果用户的连接速度较慢,并且/或者下载量较大,则这些短期内存泄漏可能会成为一个问题 – 例如,如果方向多次改变(并且不取消正在执行的任务),或者用户导航远离活动。
  • AsyncTask根据执行的平台具有不同的执行特性:API级别4之前AsyncTasks在单个后台线程上串行执行; 从API级别4到API级别10,AsyncTasks在多达128个线程的池上执行; 从API级别11起,AsyncTask在单个后台线程上执行(除非使用重载的executeOnExecutor方法并提供替代执行程序)。 当在ICS上串行运行时,工作正常的代码可能在Gingerbread上同时执行时中断,比如说,如果你有无意的执行顺序依赖关系。

如果你想避免短期的内存泄漏,在所有平台上都有明确的执行特性,并有一个基础来build立真正强大的networking处理,你可能要考虑:

  1. 使用一个为你完成这个工作的图书馆 – 在这个问题上有一个很好的networking库的比较,或者
  2. 使用ServiceIntentService来代替,也许用PendingIntent通过Activity的onActivityResult方法返回结果。

IntentService方法

下行方面:

  • AsyncTask更多的代码和复杂性,尽pipe不如你想像的那么多
  • 将排队请求并在单个后台线程上运行它们。 你可以通过用一个等效的Service实现replaceIntentService来轻松地控制这个,也许就像这样 。
  • 呃,现在我实在想不出其他人

向上方面:

  • 避免短期内存泄漏问题
  • 如果您的活动在networking操作正在进行时重新启动,它仍然可以通过onActivityResult方法接收下载的结果
  • 比AsyncTask更好的平台来构build和重用健壮的networking代码。 例如:如果你需要做一个重要的上传,你可以从一个Activity AsyncTask中完成,但是如果用户上下文切换到应用程序AsyncTask电话,系统可能会在上传完成之前closures应用程序。 杀死具有活动Service的应用程序的可能性较小
  • 如果您使用您自己的IntentService的并发版本(就像我上面链接的版本),则可以通过Executor控制并发级别。

执行摘要

你可以实现一个IntentService来很简单地在单个后台线程上执行下载。

第1步:创build一个IntentService来执行下载。 你可以通过Intent extra来告诉它下载什么东西,并且传递一个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项目可以在这里find 。

  1. 不要使用strictMode(仅在debugging模式下)
  2. 不要更改SDK版本
  3. 不要使用单独的线程

使用服务或AsyncTask

另请参阅堆栈溢出问题:

android.os.NetworkOnMainThreadException从Android发送电子邮件

在另一个线程上执行networking操作

例如:

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

并将其添加到AndroidManifest.xml

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

您使用以下代码禁用严格模式:

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

这不被推荐 :使用AsyncTask接口。

这两种方法的完整代码

基于networking的操作不能在主线程上运行。 您需要在子线程上运行所有基于networking的任务或实现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() { 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 } } 

使用Android Annotations是一个选项。 它将允许您只需在后台线程中运行任何方法:

 // normal method private void normal() { doSomething(); // do something in background } @Background protected void doSomething() // run your networking code here } 

请注意,尽pipe它提供了简单性和可读性的好处,但也有其缺点。

这发生在Android 3.0及以上。 从Android 3.0及以上版本开始,他们已经限制在主线程/ UI线程中运行networking操作(访问因特网的function)(从创build和恢复活动的方法中产生的)。

这是为了鼓励使用单独的线程进行networking操作。 有关如何以正确方式执行networking活动的更多详细信息,请参见AsyncTask 。

您不应在主线程(UI线程)上执行任何耗时的任务,如任何networking操作,文件I / O或SQLite数据库操作。 所以对于这种操作,你应该创build一个工作者线程,但问题是你不能直接从你的工作线程执行任何UI相关的操作。 为此,您必须使用Handler并传递Message

为了简化所有这些,Android提供了各种方式,比如AsyncTaskAsyncTaskLoaderCursorLoader或者IntentService 。 所以你可以根据你的要求使用这些。

spektom的顶部答案完美。

如果您正在编写AsyncTask内联而不是作为类进行扩展,并且在此之上,如果需要从AsyncTask获取响应,可以使用get()方法如下。

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

(从他的例子)

这个错误是由于在主线程中执行长时间运行的操作造成的,你可以使用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 } }); 

这只适用于定位到Honeycomb SDK或更高版本的应用程序。 针对早期SDK版本的应用程序允许在主事件循环线程上进行联网。

错误是SDK警告!

对我来说是这样的:

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

我正在testing我的应用程序的设备是4.1.2 SDK版本16!

确保目标版本与您的Android目标库相同。 如果你不确定你的目标库是什么,右键点击你的项目 – > 构buildpath – > Android ,它应该是一个打勾。

另外,正如其他人所提到的,包括访问Internet的正确权限:

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

只是明确地说明一些事情:

主线程基本上是UI线程。

所以说你不能在主线程中做networking操作就意味着你不能在UI线程中进行networking操作,这意味着你不能在一个*runOnUiThread(new Runnable() { ... }*内的其他线程,要么。

(我只是想了解为什么我在主线程以外的地方发现了这个错误,这就是为什么;这个线程有帮助;希望这个评论能帮助别人。

如果执行任务花费太多时间,则会在主线程上执行繁重任务,从而导致发生此exception。

为了避免这种情况,我们可以使用线程执行器来处理它

 Executors.newSingleThreadExecutor().submit(new Runnable() { @Override public void run() { // You can perform your task here. } }); 
  **Use like this in Your Activity** 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 inten=new Intent(getApplicationContext(),ComposeMail.class); //intent.putParcelableArrayListExtra("valuesList", value); startActivity(inten); } }).start(); } }); 

简而言之,

不要在networking线程中工作

例如,如果您执行HTTP请求,那是一个networking操作。

解:

  1. 你必须创build一个新的主题
  2. 或者使用AsyncTask类

办法:

把你所有的作品放进去

  1. 新线程的run()方法
  2. 或者 AsyncTask类的doInBackground()方法。

但:

当你从networking响应中得到一些东西,并想要在你的视图上显示它(如在TextView中的显示响应消息),你需要返回到UI线程。

如果你不这样做,你会得到ViewRootImpl$CalledFromWrongThreadException

如何?

  1. 在使用AsyncTask时,从onPostExecute()方法更新视图
  2. 或者调用runOnUiThread()方法并在run()方法内更新视图。

虽然上面有一个巨大的解决scheme池,没有人提到com.koushikdutta.ion : https : //github.com/koush/ion

它也是asynchronous的 ,使用起来非常简单

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

这工作。 只是让吕吉博士的回答稍微简单一些。

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

在Android上,networking操作不能在主线程上运行。 您可以使用Thread,AsyncTask(短期运行任务),Service(长期运行任务)来执行networking操作。

从主(UI)线程访问networking资源导致此exception。 使用单独的线程或AsyncTask来访问networking资源以避免此问题。

还有另一个解决这个问题的非常方便的方法 – 使用rxJava的并发能力。 您可以在后台执行任何任务,并以非常方便的方式将结果发布到主线程,从而将这些结果交给处理链。

第一个经过validation的答案build议是使用AsynTask。 是的,这是一个解决scheme,但现在已经过时,因为有新的工具。

 String getUrl() { return "SomeUrl"; } private Object makeCallParseResponse(String url) { return null; // } private void processResponse(Object o) { } 

getUrl方法提供了URL地址,它将在主线程上执行。

makeCallParseResponse(..) – 做实际的工作

processResponse(..) – 将处理主线程上的结果。

asynchronous执行的代码如下所示:

 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 。

您也可以使用下面的代码使用严格模式来解决此问题。 这也是解决这个问题的一个select。

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

但最好的做法是使用AsyncTask。

我们也可以使用RxJava将networking操作移动到后台线程。 而且它也相当简单。

 webService.doSomething(someData) .subscribeOn(Schedulers.newThread())-- This for background thread .observeOn(AndroidSchedulers.mainThread()) -- for callback on UI .subscribe(result -> resultText.setText("It worked!"), e -> handleError(e)); 

你可以用RxJava做更多的事情。这里有一些RxJava的链接。 随意挖掘。

Android中的RxJavaasynchronous任务

http://blog.stablekernel.com/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns/

您不能在Android上的UI线程上执行networking操作。 你将不得不使用AsyncTask类来执行networking相关的操作,例如发送API请求,从URL下载图像等,并使用AsyncTask的callback方法,你可以得到onPostExecute menthod的结果,你将在UI线程和你可以从Web服务或类似的东西填充用户界面。

示例:假设您想要从URL下载图像: https : //www.samplewebsite.com/sampleimage.jpg

解决scheme使用AsyncTask:分别。

  public class MyDownloader extends AsyncTask<String,Void,Bitmap> { @Override protected void onPreExecute() { // Show progress dialog super.onPreExecute(); } @Override protected void onPostExecute(Bitmap bitmap) { //Populate Ui super.onPostExecute(bitmap); } @Override protected Bitmap doInBackground(String... params) { // Open URL connection read bitmaps and return form here return result; } @Override protected void onProgressUpdate(Void... values) { // Show progress update super.onProgressUpdate(values); } } } 

注意:不要忘记在Android清单文件中添加Internet权限。 它会像魅力一样工作。 🙂

当应用程序尝试在其主线程上执行联网操作时,会引发此exception。 如果你的任务花费了五秒以上,那就需要一个closures的力量。

AsyncTask运行你的代码:

 class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> { protected RSSFeed doInBackground(String... urls) { // TODO: Connect } protected void onPostExecute(RSSFeed feed) { // TODO: Check this.exception // TODO: Do something with the feed } }