当一个应用程序启动之后,android系统会为这个应用程序创建一个主线程(Main Thread),它负责渲染视图,分发事件到响应监听器并执行,对界面进行轮询的监听。因此,一般也叫做“UI线程”(UI Thread)。
android系统不会给应用程序的多个元素组件建立多个线程来执行。一个视图(Activity)中的多个view组件运行在同一个UI线程当中。因此,多个view组件的监听器的执行可能会相互影响。
如果在UI线程中做一些比较耗时的操作,比如访问网络或者数据库,都可能阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程阻塞时间太长(超过5秒),android系统会弹出ANR(application not responding)的对话框,询问用户是否关闭应用程序。
针对此问题,Android的单线程模型有两条原则:
1,不要阻塞UI线程;
2,不要在UI线程之外的其他线程中,对视图当中的组件(主要是这两个包中的组件:android.widget和android.view)进行设置。
可能很多同学想到解决第一个问题的办法是new一个新的线程进行耗时操作,但这往往会触发第二个问题,系统会抛出异常:Only the original thread that created a view hierarchy can touch its views(只有创建view的那个线程(UI线程)才能对其进行修改)。
当然,android官方提供了解决方案:
一、通过View.post(Runnable)方法
public void onClick(View v) {
new Thread(new Runnable() {//new一个新线程来执行耗时操作,解决问题一
@Override
public void run() {
try{
Thread.sleep(5000);//执行耗时操作
}catch(InterruptedException e){
e.printStackTrace();
}
v.post(new Runnable() {//使用View.post(Runnable)进行组件设置,解决问题二
@Override
public void run() {
TextView view = (TextView) v;
v.setText("设置组件")
}
});
}
}).start();
}
这种解决方法中,UI线程与我们新建的线程之间的关系类似于生产者与消费者之间的关系,新线程通过View.post(Runnable)方法在任务队列中加入任务,而UI线程对任务队列进行轮询,有任务的话就拿出来执行,修改界面。
但这种解决方法可读性和维护性较差。
二、继承AsyncTask类(异步任务)
在activity中建一个内部类,继承AsyncTask,重写doInBackground()和onPostExecute()方法
private class task extends AsyncTask<String, Void, String>{
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(5000);// 执行耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return "通过AsyncTask设置";
}
@Override
protected void onPostExecute(String result) {
textView.setText(result);// textView写成Activity的成员变量,方便内部类调用
}
}
doInBackground的返回值会做为onPostExecute的参数执行。
然后我们只需要在需要调用的时候new task().execute()就可以执行了。
第二种方案其实是对第一种方法进行了封装,提高了代码可读性。