关于android.os.NetworkOnMainThreadException
16lz
2021-01-26
关于android开发中线程安全的问题想必大家都碰到过。
特别是android.os.NetworkOnMainThreadException
在android的不同版本出错误地方是访问网络的webservice有问题。
很多情况下,我们通常是访问网络获取数据,然后根据数据来改变UI的显示。
比如ListView或者TextView,我们获取数据后,需要在这些控件上面显示。
在Android2.3以下的版本中,我们可以直接写流水线的方式,获取数据,给控件赋值,既可以完成。
但是在Android2.3以上的版本(包含2.3)就不能这样写了。
这样写在访问网络时会提示android.os.NetworkOnMainThreadException
在给控件赋值时会提示Only the original thread that created a view hierarchy can touch its views.
这主要是Android的相关View和控件不是线程安全的。
针对这个问题,解决方案有如下几种:
1)用异步任务去解决,开启一个异步进程去进行数据访问与控件赋值。
代码如下:
/** * 点击登录开线程,出现进度提示框 * * @author weis * */ public class loginTask extends AsyncTask<String, Void, String> { private Exception _exception = null; private String errMsg = ""; @Override protected void onPreExecute() { loading.show(); } @Override protected void onPostExecute(String ss) { loading.dismiss(); if (null != _exception) { ToastUtil.show(Login.this, errMsg + _exception.getMessage()); }else{ ToastUtil.show(Login.this, "登录成功!"); } } @Override protected String doInBackground(String... params) { // TODO Auto-generated method stub try { LoginOn(); } catch (UnknownHostException hostExp) { _exception = hostExp; errMsg = "找不到服务器:"; } catch (Exception e) { _exception = e; errMsg = "登录失败: "; } return null; } }
2)也是使用线程的方式Thread,不过不是上面的处理方式,而是通过Hander,Runnable 去处理。
protected void Operation() { Thread t = new Thread() {public void run() {try {strSchoolYear = semester.getCurrentSemester().getValue();} catch (Exception e) {ToastUtil.show(getBaseContext(), e.getMessage());}handler.post(runnable); }};t.start();}Handler handler = new Handler();Runnable runnable = new Runnable() {public void run() {schoolYearTextView.setText(strSchoolYear);}};
3)在2.3以上版本中先加入了 新的类 StrictMode,用于捕捉发生在应用程序主线程中耗时的磁盘、网络访问或函数调用,可以帮助开发者改进程序,使主线程处理 UI 和动画在磁盘读写和网络操作时变得更平滑,避免主线程被阻塞。
本人比较懒,比较喜欢这种方式
编写一个基类,在基类的OnCreate方法中加入重构StrictModel的代码,就可以继续用以前的流水线的方式编码了。
public void onCreate(Bundle savedInstanceState) {String strVer=android.os.Build.VERSION.RELEASE;//重构StrictModestrVer=strVer.substring(0,3).trim();float fv=Float.valueOf(strVer);if(fv>2.3){StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectAll().build());}super.onCreate(savedInstanceState);}
detectAll 是允许所有操作,包括磁盘访问,网络访问等。 这种方式要谨慎使用。
弊端很明显,在主线程中读取数据,读取磁盘可能比较耗时,容易引起主线程堵塞,用户体验不好。
更多相关文章
- Android实现一个通用的PopupWindow
- Android(安卓)PullToRefresh下拉刷新控件的简单使用
- 转载-runOnUIThread解析
- Android(安卓)获取OnItemClick事件中组件的内容
- Android周报第四期
- 让应用程序具体相应权限
- Android(安卓)ANR 详解
- Android(安卓)自定义 View - 颜色选取器(水平、竖直)
- Android知识体系图