一、什么是Handler
首先来看一下Google官方对Handler的定义:
Handler是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每一个Handler实例之后会关联一个线程和该线程的消息队列。当你创建一个Handler的时候,从这时开始,它就会自动关联到所在的线程/消息队列,然后它就会陆续把Message/Runnalbe分发到消息队列,并在它们出队的时候处理掉。
从官方的定义可以就看出Handler的存在与线程密不可分,Handler是Android消息机制的上层接口,所有在开发过程中只需要和Handler交互即可。Android应用涉及到的网络加载等耗时操作时是不能在主线程进行的,因为耗时操作会造成线程阻塞,只允许在主线程更新UI,Android的UI控件不是线程安全的,如果在多线程的并发访问中可能会导致UI控件处于不可预期的状态。而Handler的作用相当于线程通信的桥梁,而不是说Handler就是专门用来更新UI的,但Handler的主要原因也是为了处理子线程无法访问UI的矛盾。Handler主要作用是将一个任务切换到指定的某个线程中去执行,比如在子线程向服务器进行一些拉取信息的工作,在拉取信息完毕之后要在UI上进行显示,就需要利用Handler将访问UI的工作切换到主线程。
二、Handler深入
Handler在使用的过程中离不开Looper、MessageQueue和Message这三者,Handler的运行需要底层的MessageQueue和Looper的支撑,他们之间的关系如下图:
它们各自的作用分别是:
- Handler:负责发送和处理消息。
- Message:用来携带需要的数据。
- MessageQueue:消息队列,队列里面的内容就是Message,提供插入和删除的工作,它只是一个消息的储存单元,不能去处理消息。
- Looper:消息轮巡器,Looper填补了MessageQueue不能处理消息的功能,Looper负责不停的从MessageQueue中取Message,如果有就取出处理消息,否则就阻塞一直等待消息。
Handler
Handler的工作主要包含消息的发送和接收过程。消息的发送通过post的一系列方法和send的一系列方法进行消息的发送,post系列的方法最终也是调用send系列的方法实现。调用的关系如图:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
从send的源码可以看出Handler在发送消息的过程其实是调用enqueueMessage方法将这个消息放到消息队列中,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息就开始进行处理,最终交给Handler处理,调用dispatchMessage方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以发现,Handler发送消息的过程仅仅是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到这条消息就开始处理,最终结果由Looper交给Handler处理,既Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段。
Looper
每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列中的Message。它的特点是跟线程绑定,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。
那么就会有人问,为什么我们使用Handler的时候从来都不需要创建Looper呢?这是因为在主线程中,ActivityThread默认会把Looper初始化好,prepare以后,当前线程就会变成一个Looper线程。Handler的工作需要Looper,没有Looper的线程会报错。Looper会不停的从消息队列中查看是否有新的消息,如果有新的消息就会立即处理,否则会一直阻塞在那里。Looper的构造器创建了一个MessageQueue,然后将当前线程的对象保存起来。
Looper的最重要的一个方法是loop方法,只有调用了loop后,消息循环系统才会真正地起作用。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue
MessageQueue是一个消息队列,用来存放Handler发送的消息。每个线程最多只有一个MessageQueue。MessageQueue通常都是由Looper来管理。主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper。MessageQueue主要包含两个操作,enqueueMessage和next,其中前者的作用是往消息队列中插入一条信息,而next的作用是从消息队列中取出一条消息并将其从消息队列中移除,当消息队列为空时会next方法会一直阻塞在这里,当有新消息来的时候,next会返回这条消息并将其从列表中移除。MessageQueue的内部实现并不是队列,而是通过一个单链表的实现,单链表在插入和删除上比较有优势。
enqueueMessage:
在enqueueMessage中首先判断,如果当前的消息队列为空,或者新添加的消息的执行时间when是0,或者新添加的消息的执行时间比消息队列头的消息的执行时间还早,就把消息添加到消息队列头(消息队列按时间排序),否则就要找到合适的位置将当前消息添加到消息队列。
next
next方法是一个无限循环的方法,如果消息队列没有消息,next方法会一直阻塞,当新消息到来的时候,next方法会返回这条消息并将其从单链表中移除。
Message
消息对象,就是MessageQueue里面存放的对象,一个MessageQueu可以包括多个Message。当我们需要发送一个Message时,我们一般不建议使用new Message()的形式来创建,更推荐使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。
三、Handler的用法
我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。