关于android.os.NetworkOnMainThreadException

关于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;
		//重构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 是允许所有操作,包括磁盘访问,网络访问等。

这种方式要谨慎使用。

弊端很明显,在主线程中读取数据,读取磁盘可能比较耗时,容易引起主线程堵塞,用户体验不好。



你可能感兴趣的:(android)