我如何解决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处理,你可能要考虑:
- 使用一个为你完成这个工作的图书馆 – 在这个问题上有一个很好的networking库的比较,或者
- 使用
Service
或IntentService
来代替,也许用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 。
- 不要使用strictMode(仅在debugging模式下)
- 不要更改SDK版本
- 不要使用单独的线程
使用服务或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提供了各种方式,比如AsyncTask
, AsyncTaskLoader
, CursorLoader
或者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操作。
解:
- 你必须创build一个新的主题
- 或者使用AsyncTask类
办法:
把你所有的作品放进去
- 新线程的
run()
方法 - 或者 AsyncTask类的
doInBackground()
方法。
但:
当你从networking响应中得到一些东西,并想要在你的视图上显示它(如在TextView中的显示响应消息),你需要返回到UI线程。
如果你不这样做,你会得到ViewRootImpl$CalledFromWrongThreadException
。
如何?
- 在使用AsyncTask时,从
onPostExecute()
方法更新视图 - 或者调用
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任务
您不能在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 } }
- 如何在Python中打印错误?
- 在Java中,什么时候应该创build一个检查的exception,什么时候应该是一个运行时exception?
- 尝试/在Python除外:你如何正确地忽略例外?
- 困惑于std :: runtime_error与std :: logic_error
- 如果catch和finally块都抛出exception,会发生什么?
- 为什么exception处理不好?
- java.rmi.ServerException:在服务器线程中发生RemoteException(ClassNotFoundException)
- 抛出ArgumentNullException
- 为什么析构函数不被exception调用?