关于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; } }
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); } };
本人比较懒,比较喜欢这种方式
编写一个基类,在基类的OnCreate方法中加入重构StrictModel的代码,就可以继续用以前的流水线的方式编码了。
public void onCreate(Bundle savedInstanceState) {
String strVer=android.os.Build.VERSION.RELEASE;
//重构StrictMode
strVer=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 是允许所有操作,包括磁盘访问,网络访问等。
这种方式要谨慎使用。
弊端很明显,在主线程中读取数据,读取磁盘可能比较耗时,容易引起主线程堵塞,用户体验不好。