标签:
相信很多Android开发者很多都遇到过android.os.NetworkOnMainThreadException 这个异常,意思就是主线程进行网络操作异常。这个问题比较简单,但是网络上有着鱼龙混杂的答案,这里想花点时间做一个比较完整的描述。
在早期的Android版本(2.3之前)中,Google并没有提供一个很严格的程序编写要求,所以在那时我们可以在主线程中执行本地IO操作,网络操作等这些不规范的行为。后来在2.3的姜饼(GINGERBREAD)开始提供了一个开发者工具,这就是StrictMode严格模式。
严格模式可以帮助开发者发现主线程中的磁盘操作和网络操作,开发者根据严格模式的输出信息可以改善程序来更好地响应用户操作,来较少ANR(程序未响应)的问题。
android.os.NetworkOnMainThreadException这个异常从Android 3.0(API 11)引入,出现情况为主线程进行网络操作。
if(Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD){ ThreadPolicy.Builder threadPolicyBuilder = new StrictMode.ThreadPolicy.Builder(); threadPolicyBuilder.detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog(); StrictMode.setThreadPolicy(threadPolicyBuilder.build()); VmPolicy.Builder vmPolicyBuilder = new VmPolicy.Builder(); vmPolicyBuilder.detectLeakedSqlLiteObjects().penaltyLog(); if( Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB){ vmPolicyBuilder.detectLeakedClosableObjects(); } StrictMode.setVmPolicy(vmPolicyBuilder.build()); }
下面的这段代码会让严格模式允许所有的磁盘操作和网络操作。但是这并没有改变真正解决问题,主线程中照样还是有网络操作,可能导致程序出现未响应的情况。所以这是一个很糟糕的解决方法,问题的解决思路应该是将网络操作移到非主线程进行,而不是这种掩耳盗铃的做法。
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);
p.s. 这个很笨的方法居然在Stackoverflow上有很多人认为有用,难以理解。
先实现一个我们用来测试的请求网络的方法
private void doGetRequest(){ HttpGet method = new HttpGet("http://droidyue.com"); Abstract HttpClient http = new DefaultHttpClient(); try{ HttpResponse response = http.execute(method); Log.i(LOGTAG,"doGetRequest responseCode=" + response.getStatusLine().getStatusCode() + "ThreadInfo = " + Thread.currentThread()); } catch(ClientProtocolExceptione) { e.printStackTrace(); } catch(IOExceptione){ e.printStackTrace(); } }
使用AsyncTask可以将网络操作移到了AsyncTask的线程,可以避免NetworkOnMainThreadException异常。
new AsyncTask<Void,Integer,Void>(){ @Override protected Void doInBackground(Void...params){ doGetRequest(); return null; } }.execute();
更详细的关于AsyncTask请参考Android中糟糕的AsyncTask
既然AsyncTask可能导致内存泄露并且不适用于长时间操作,那么这样呢
new Thread(){ @Override public void run(){ super.run(); doGetRequest(); } }.start();
这样还是不够好,虽然单独线程可以处理长时间的操作,但是问题还是依旧
解决了上述的内存泄露基本可以做到比较完美的实现,或者使用Loaders实现也不错。关于线程重用问题,可以使用Executors.newSingleThreadExecutor()来解决。具体方案因情况而定。
Android.os.NetworkOnMainThreadException
标签:
原文地址:http://www.cnblogs.com/abapscript/p/4881794.html