怎样理解Android中的Handler,MessageQueue、Runnable与Looper?
简单来讲,用一句话概括就是:
Looper不断获取MessageQueue中的一个Message(Runnable会包装成Message),然后由Handler来处理。
这其实就是进程/线程跑起来的基础,我们称为消息循环处理机制。其实任何系统能够跑起来的本质就是依赖于这样一个机制。
所以我们第一个要搞清楚的问题是在Android中消息循环处理机制是怎么搭建起来的?
线程是CPU调度的基本单位,也就是说线程相当于一个平台,我们利用这个平台做各种各样的事情,显然消息循环机制(Looper)也是在线程上完成的。所以我们首先看下在线程里是怎么使用Looper的?举个简单的例子:
class LooperThread extends Thread{
@Override
public void run() {
Looper.prepare();
Handler h = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}
从上面的代码可以看出,Looper的使用是非常简单的,就3个步骤。
第一步:Looper.prepare();
准备工作,看下源码里面做了什么事情?
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
首先看下sThreadLocal对象是什么东西?
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
哦,原来是ThreadLocal对象,这里ThreadLocal的意义在于为每个线程隔离Looper对象,也就是每个线程对应一个Looper对象,sThreadLocal就是用来统一管理每个线程的Looper对象,确保每个线程的Looper对象是独立的。所以Looper.prepare();的作用在于为线程创建一个Looper对象,可以通过sThreadLocal.get()来获得。从代码里可以看出,一个线程对应一个Looper对象。然后在Looper的构造器里初始化了MessageQueue。
第二步:创建处理消息的Handler;
你肯猜到了,Handler和Looper存在某种关系,不然这个消息循环处理是怎么连接起来的。所以我们从Handler的构造方法入手。
public Handler() {
//省略部分代码
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
从上面的代码里我们得到以下几条信息:
1、在Handler里通过Looper.myLooper()得到了线程的Looper对象;
public static Looper myLooper() {
return sThreadLocal.get();
}
2、在Handler里维护了一个MessageQueue mQueue,指向Looper对象里的MessageQueue。
3、有一个回调接口对象 mCallback
好,现在Handler、Looper和MessageQueue算是有了一些联系。
下面看第三步:Looper.loop();
前面两步都是准备工作,这一步开始真正做事情了,我们来看下loop()方法里的主要流程。
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}
从上面可以看出,消息循环的机制就是从Looper对象中拿到消息队列MessageQueue,然后不断的从MessageQueue里拿到消息,然后由handler(msg.target)去处理(dispatchMessage(msg))。
从创建Looper对象和Handler对象,到handler和Looper之间联系起来,再到消息循环处理,就这样,消息循序处理机制搭建起来了,线程跑起来了,整个流程我们似乎也明白了,但其实仔细想想,我们忽略了很多细节:
1、前面我们讲了从消息队列MessageQueue中取出消息message交由handler处理,但好像没看到过消息是怎么放进消息队列的?Message和handler是怎么联系起来的也不清楚?
我们知道handler是用来处理消息的,其实handler还有另外一个作用—-发送消息:将Message压入MessageQueue中。Handler发送消息用到的API分为两个系列,Post系列和Send系列。
Post系列:
public final boolean post(Runnable r);
public final boolean postAtTime(Runnable r, long uptimeMillis);
…………
Send系列:
public final boolean sendMessage(Message msg);
public final boolean sendEmptyMessage(int what);
public final boolean sendMessageDelayed(Message msg, long delayMillis);
public boolean sendMessageAtTime(Message msg, long uptimeMillis);
……………
Post和Send两个系列的共同点是它们都负责将某个消息压入MessageQueue中;区别在于后者处理的函数参数直接是Message,而Post处理的参数是Runnable,将Runnable转换成Message,再调用Send系列函数来执行下一步。
我们挑选第一个Post函数来分析下其流程:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
首先通过方法getPostMessage(r)将Runnable对象封装成Message ,再通过对应的send函数把它推送到MessageQueue中;
private final Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
从getPostMessage(r)方法中我们可以知道以下几点
1)Android系统会维护一个全局的Message池,当用户需要使用Message时,可以通过obtain直接获得,而不是自行创建。这样的设计可以避免不必要的资源浪费。
2)Runnable对象赋给的Message的callback,在Message类中callback的定义如下:
Runnable callback;
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
SystemClock.uptimeMillis():从手机开机到现在的毫秒数,不包括睡眠时间。
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
return sent;
}
还记得mQueue吗?在handler的构造器中将Looper里的MessageQueue赋给了mQueue。
Message和handler也在这里联系起来了:msg.target = this;
最后queue.enqueueMessage(msg, uptimeMillis)将消息压入队列。
2、对于handler处理消息,上面我们是一笔带过的,msg.target.dispatchMessage(msg); 那消息处理的流程到底是怎么样的呢?
在Handler类中对消息处理有两个方法:
public void dispatchMessage(Message msg);//对Message进行分发
public void handleMessage(Message msg);//对Message进行处理
Looper从MessageQueue中取出一个Message后,首先会调用Handler. dispatchMessage进行消息派发,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先判读msg.callback是否为空,还记得这个吗?前面提到过msg.callback就是Post系列传入的Runnable对象,所以Post系列发送的消息msg.callback不为空,走handleCallback(msg),
private final void handleCallback(Message message) {
message.callback.run();
}
直接调用run方法执行任务,消息处理结束。
Send系列发送的消息callback为空,所以走else,先判断mCallback是否为空,mCallback对象是什么?其实这是一个回调接口对象,接口里也有一个handleMessage(msg)方法用来处理消息,
public interface Callback {
public boolean handleMessage(Message msg);
}
这个对象是在Handler的构造器中传入的,传了就有,没传就没有,作用在于有了回调接口来处理消息,就不用写Handler的子类来实现handleMessage(Message msg)方法了。
如果mCallback为空,或者mCallback.handleMessage(msg)消息没处理成功,就走Handler类中的handleMessage(msg)了,
public void handleMessage(Message msg) {
}
在Handler类中,这是一个空的方法,子类重写这个方法去完成消息处理的逻辑。
讲到这里,你可能会很奇怪,Handler同时负责发送消息和处理消息,为什么不直接操作,而是大费周章地先把Message压入MessageQueue,再取出来处理?其实这体现了程序设计的一个良好的习惯,即“有序性”。如果世界没有了秩序,那将乱的一塌糊涂,程序也是如此。
3、我们知道,调用Looper.loop()后消息循环处理就开始了,那循环什么时候退出了?
从loop()方法里可以看到有一种情况可以退出循环,就是当msg.target == null ;时,但是上面我们讲过msg.target的赋值是在sendMessageAtTime(Message msg, long uptimeMillis)方法里,而不管是Post还是Send发送消息都会走到这个方法,从而给msg.target赋值,所以正常情况下消息处理循环是不会退出的,怎么办了?其实在Looper类中有一个quit()方法,我们看下里面做了什么事情?
public void quit() {
Message msg = Message.obtain();
mQueue.enqueueMessage(msg, 0);
}
在这里面直接往MessageQueue中压入一条消息,这样msg.target就为空了,当循环到这条消息时,就退出了循环,所以如果你想终止消息处理循环,请调用这个方法即可。
现在,我们基本上理清了Android中的Handler,MessageQueue、Runnable与Looper之间的关系和它们之间的流程机制了。每个Thread只对应一个Looper;每个Looper只对应一个MessageQueue。把一个Thread比作一座城市,Looper是城市的户籍名称;MessageQueue是这座城市的唯一地标性建筑;而Hander就是人口,在一座城市,可以有本地人口,也可以有外地人口,但每个人都有户籍,标识他属于哪座城市;Message就是货物,不区分属地,携带在人的身上,在城市或城市间流通。
最后提一点,在Looper类中还有这样两个方法:
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
public synchronized static Looper getMainLooper() {
return mMainLooper;
}
这两个方法就是我们熟悉的UI主线程专用的方法了,
new Handler(getMainLooper())就代表在主线程运行了。