Handler消息机制是Android中很重要的一个知识点,之前网上有很多同学分享了大量的博客介绍handler机制,那么为什么我要再写一篇呢?因为我看过的博客发现分析的干巴巴的,不容易记忆。所以呢,这次我花时间做了一张消息机制的时序图,为的就是看图给右脑记忆,从此不在忘记!!!
好了,废话不多说,先看时序图,下面我会根据图讲解串通:
从哪看起呢?大家分析Handler消息机制的时候要么从sendMessage()分析起来,要么从Looper.prepare()开始,我喜欢从后者开始,因为sendMessage()执行的前提就是Looper调用了loop()方法,不然也收不到消息。我们就先从Looper说起,因为Looper执行loop方法之后才能正常接收到消息并执行后续操作。
主线程Looper初始化
我们先看图中的Looper对象,在Looper对象的第一个控制焦点,即上图中 “ActivityThread创建,初始化Looper,新建MessageQueue,即Queue” 焦点,由这个焦点可看出我们谈论的是主线程中的Looper,所以上图中的Looper对象就是主线程的。子线程的其实类似,就是Looper的初始化需要我们自己调用,我们先看主线程的。
主线程中的Looper什么时候创建的呢?因为我们代码中并没有为UI线程创建Looper,实际上创建应用程序的时候系统就已经为我们创建好了UI线程的Looper,看代码:
public static void main(String[] args) {
// ...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
// ...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这是ActivityThread的main()方法,可以认为就是应用程序创建的入口,main()方法里我们先调用了Looper.prepareMainLooper()方法,准备好Looper之后,最后调用了Looper.loop(),这时候主线程(即UI线程,后续不在解释)的Looper就开始了循环,也就意味着我们可以往主线程发消息了。
Looper.prepareMainLooper()内部还是调用了prepare()方法,往ThreadLocal对象里设置了一个Looper实例,如下:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
我们看下Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
构造方法里会创建一个MessageQueue对象,顾名思义,就是用来放Message的队列,Looper调用loop()方法之后开始循环,就会从这个MessageQueue里面取消息。有取必然有放,我们看完了Looper的初始化,Looper循环也运行起来了。所以接下来看发消息。
Handler发送消息
需要发送消息,那么就必须要创建Handler,所以我在图中加了一个Activity对象,并调用Handler的构造方法创建Handler对象,Handler的构造方法有个点需要注意:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
//标注1
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//标注2
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
创建Handler最终会调用到上面的构造方法,当然创建子线程接收消息的Handler除外。标注1处通过Looper.myLooper()获取到Looper对象,myLooper方法内部还是从ThreadLocal里面取的,所以拿到的当然是主线程的Looper了。标注2处拿到Looper的MessageQueue,只要拿到这个MessageQueue,我们在Handler里面就可以往主线程的MessageQueue里面放Message了。
好了,我们终于可以开始发消息了
看图中Thread对象的第一个焦点,这个Thread对象就是模拟工作线程,第一个焦点调用Message.obtain()方法获得一个msg,然后调用Handler的sendMessage()方法(这里不重要的点我就不在贴源码,因为图中生命线和焦点就是源码方法调用的时序,大家在看的时候可以对照源码),sendMessage()内部调用Handler的sendMessageDelayed()方法,到达下一个焦点,sendMessageDelayed()方法又调用了Handler内部的sendMessageAtTime()方法,sendMessageAtTime()方法接着调用了Handler本身的enqueueMessage()方法,enqueueMessage()我要贴下代码,因为这里有个精彩的地方:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这里我们发送的msg把它的target属性指向了Handler自身,为什么要这样做呢,就是为了Looper接收到消息之后回调Handler的callback,后面我们把消息发到Looper的时候会细说,继续看,enqueueMessage()方法调用了queue.enqueueMessage(msg, uptimeMillis)方法,这个queue就是我们创建Handler时候的MessageQueue,我们看MessageQueue的enqueueMessage()方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//...
msg.when = when;
Message p = mMessages;
boolean needWake;
//标注1
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//标注2
for (;;) {
prev = p;
p = p.next;
//标注3
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
代码挺长,实际上就是入队操作,这里有个mMessages,并且p指向了mMessage,我们可以把它理解为是待执行的message队列,该队列是按照when的时间排序的且第一个消息是最先执行。
我们看标注1处,有三个条件,如果mMessages对象为空,或者when为0也就是立刻执行,或者新消息的when时间比mMessages队列的when时间还要早,符合以上一个条件就把新的msg插到mMessages的前面 并把next指向它,也就是msg会插进上图中队列的最前面,等待loop的轮询。
如果上面的条件都不符合就进入else代码中,我们可以看到标注2处有个for的死循环遍历已有的message对象,其中标注3中有个if语句when < p.when when是新消息的执行时间,p.when是队列中message消息的执行时间,如果找到比新的message还要晚执行的消息,就执行 msg.next = p; prev.next = msg;也就是把插到该消息的前面,优先执行新的消息。好了,到这里我们发送的消息就到了队列里面,发送消息已经完结了,下面就等Looper循环到我们的消息就行了。
Looper循环
Looper调用loop()方法之后就是一个死循环,如下是loop()方法源码:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//...
for (;;) {
//标注1
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
try {
//标注2
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//...
}
}
代码很长,我只贴出有用的,可以看到标注1处,会从queue里面取出msg,然后看标注2,会调用msg的target属性的dispatchMessage(msg)方法,首先这个target我们上面提了一下,就是在Handler的enqueueMessage()方法里我们把Handler自身赋值给了msg的target属性,那么loop循环里调用到标注2处就又回到了Handler的dispatchMessage(msg)方法。
分发消息
dispatchMessage(msg)就是分发消息的处理,我们看源码:
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自身的回调方法,否则进入else里面
else里面先判断mCallback是否为空,这个mCallback在Handler构造方法里可以传入,如果mCallback不为空就调用mCallback回调,否则调用handleMessage(msg)方法。到这个回调,消息已经接收到了,下面就是我们自己的业务逻辑处理了。
我写这篇博客的目的其实还是上面的流程图,如果大家把流程图弄懂了,以后自然就记住了。