当一个应用被启动,系统就会创建一个线程来执行这个应用,这个线程被称为主线程。这个线程非常的重要,它的作用主要有以下两点:
1、它负责把事件分发给对应的UI组件,包括绘制事件。
2、它负责应用与UI之间的交互,这些UI就是android.widget和android.view包中的组件。
总得来说,它就是负责事件的分发和交互,它跟用户界面UI组件密切相关,所以主线程也被称为UI线程。
运行在同一进程的所有组件都是在UI线程中进行实例化的,系统对每个组件的调用也会从这个线程进行分发,另外系统的回调方法也都是运行在这个线程。
例如,当用户点击屏幕上的一个Button,UI线程就会把这个点击事件分发给Button,这个Button接收到这个事件之后就会设置它的点击状态并且发送一个重绘请求消息到一个事件队列中去,UI线程从事件队列中取出这个请求消息并且通知Button进行重绘。
可以看到它是一种单线程模型,如果你所有的操作都放在UI线程中,包括一些耗时操作,例如网络和数据库操作,那么你的UI线程就很可能出于阻塞状态,当线程被阻塞,那么没有事件被分发,包括绘制事件,如果UI线程阻塞超过5秒,系统就会弹出应用无响应ANR对话框。
由于UI组件不是线程安全的,所以不能在UI线程以外的线程中来对UI进行操作,也就是说所有的操作都必须在UI线程中完成。
对于Android的这种单线程模型,下面有两点规则:
1、不要阻塞UI线程
2、必须在UI线程中进行UI操作。
从上面就可以看出,对于一些耗时的操作,一定不要放在UI线程中,你可以单独启动一个线程来进行异步的操作,如下所示。
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
上面的代码满足了规则1,但是它违背了规则2,因为ImageView的操作属于UI操作。
那么我们应该怎么做呢?下面有三种方式。
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
下面就对上面的例子进行改进。
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap =
loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
另外我们也可以使用一个已经封装好的类AsyncTask,它包括UI更新以及后台耗时操作的处理,具体可以看看 Android中AsyncTask源码解析。