Handler源码分析之一 流程简析
本文讲解Handler的源码,ok,现在先来看Handler的使用。
Handler handler = new Handler(Looper.getMainLooper());
handler.sendMessage(message);
2
# 1 构造函数分析
首先我们来看构造函数,构造函数有很多个重载版本,无非就是无参的调用有参的,少参的调用多参的,这是很常用的一个套路,比如View的构造函数就是这样,这样的好处就是,只需要在最多参的构造函数中 调用一次初始化操作就可以,其他少参的或无参的也会初始化到。现在我们来看最多参的构造即可:
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
2
3
4
5
6
嗯,很简单,初始化了mLooper, mQueue,mCallback,以及一个mAsynchronous的boolen值,下面对这些变量进行讲解:
mLooper: 一个Looper,用来对消息进行循环检测,有消息就取出处理,没有就等待。
mQueue: 一个MessageQueue,是一个队列,用来保存Message。
mCallback: 一个callback,用于触发Handler的回调,下面是他的定义,都很熟悉的,不再废话。
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
2
3
isAsynchronous:一个boolean值,用来定义消息是否是异步的,这个后面再讲。
好,现在我们来看第一个参数looper从哪里来,点击Looper.getMainLooper(),可以看到:
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
2
3
4
5
哦,这里直接return了sMainLooper,然后看sMainLooper被赋值的地方:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
2
3
4
5
6
7
8
9
这里先调用perpare(false),然后做个初始化判断,如果已经初始化过了,就抛出异常,然后对sMainLooper进行赋值,好,我们先不看prepare(),只看myLooper():
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
2
3
就这一句,既然有get(),肯定有set(),我们找找这个sThreadLocal在哪里set()的:
static final ThreadLocal<Looper> sThreadLocal = new 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));
}
2
3
4
5
6
7
哦,原来在prepare()里面set的,这就对上了,在prepareMainLooper()里面,先使用prepare(false),创建一个Looper然后使用sThreadLocal.set(looper),保存到sThreadLocal中, 然后使用sThreadLocal.get()赋值给sMainLooper。这里有个问题:为什么不这样写:
sMainLooper = new Looper(quitAllowed);
多简单!!!而非要:
sThreadLocal.set(new Looper(quitAllowed));
sMainLooper = sThreadLocal.get();
2
这个问题我们先记录一下:问题1 为什么Android要把Looper存在ThreadLocal里面?,先回到我们的主线,我们现在绕了一大圈,找到了第一个Handler的第一次个参数mLooper在这里被new出来了,我们看看new Looper(quitAllowed);做了什么:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
2
3
4
哦,第一:创建了一个MessageQueue();第二:保存了当前线程。
那这个线程是哪个线程啊?就是new Looper()的那个线程啊。
new Looper()是在prepare()中调用的,prepare()是在prepareMainLooper()中调用的,那就是说这个线程就是prepareMainLooper()被调用的线程,那prepareMainLooper()在哪里被调用的?
在这里,ActivityThread的main方法中:
public static void main(String[] args) {
...
省略若干代码
...
Looper.prepareMainLooper();
...
省略若干代码
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2
3
4
5
6
7
8
9
10
11
12
哦,找到了,这个ActivityThread.main()又是在哪里被调用的?这个是在当前进程被创建的时候调用的,也就是Android应用进程的入口,这个后面再说,现在知道了 Looper中的线程就是这个线程,这个线程是谁呢?就是UI线程,也就是主线程!可以先留个问题:问题2 ActivityThread.main()在哪里被调用? 好,现在我们来总结一下:我们找到了Handler的第一个参数Looper被new出来了,然后他保存了当前的线程,然后还new出来一个MessageQueue(),那么我们看看MessageQueue()的构造吧:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
2
3
4
很简单,就保存了一个参数,然后调用了一个nativeInit()方法,Handler的第一个参数mLooper已经初始化了,现在我们来看第二个参数mQueue:
mQueue= looper.mQueue;
就是把looper的mQueue拿过来保存一下,那第三个参数callback和第四个参数呢,都是保存一下ok了,好,Handler的构造函数完毕。说白了就是:
- 1 创建Looper并保存
- 2 创建MessageQueue并保存
# 2 调用流程分析
我们怎么使用Handler的呢?很多方法,要么: 1 handler.sendMessage(message);要么:2 handler.post(runnable),当然还有其他的使用方法,其实道理都是一样的,我们来看最普遍的用法: handler.sendMessage();
这里先放一个Message的简略类信息:
public final class Message implements Parcelable {
public int what; //不BB了,都知道的
public long when; //指定此message执行的时间,到了when这个时间就需要执行
int flags; //用于控制状态
Handler target; //被哪个Handler执行
Runnable callback; //回调
Message next; //指向下一个Message
***省略其他成员变量***
}
2
3
4
5
6
7
8
9
点进去看:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0); //这里直接把0当作delay传入
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); //这里用当前时间+需要延迟的时间传入
}
public boolean sendMessageAtTime(@NonNull 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); // 这个queue就是第一步初始化Looper时候的那个
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
又是3连调,我们只看sendMessageAtTime(msg, uptimeMillis)即可,其实就是一句话: enqueueMessage(queue, msg, uptimeMillis);继续看:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this; //将msg.target赋值为当前Handler
if (mAsynchronous) { //这个我知道,Handler构造函数的第四个参数
msg.setAsynchronous(true); // 这tm是啥意思,算了不管了
}
return queue.enqueueMessage(msg, uptimeMillis); //这queue是MessageQueue,就是Looper被new出来创建的那个
}
2
3
4
5
6
7
逻辑也很简单: msg.target = this,保存了当前Handler,然后又调用了MessageQueue的enqueueMessage(),好,我们看下这个函数:
boolean enqueueMessage (Message msg,long when){ //这里的when指的是要执行的时间
***省略部分代码***
synchronized (this) {
msg.markInUse();
msg.when = when; //保存要执行的时间,系统时间在when的时候,需要执行这个msg
Message p = mMessages; //这是一个成员变量: Message mMessages,保存当前的消息队列的队头
boolean needWake; //是否需要唤醒
if (p == null || when == 0 || when < p.when) {
//我们看下这个if条件:
//1 p == null就意味着 mMessages == null; 也就是说队头是null,也就是说本次enqueue的msg是第一个消息
//2 when == 0,这个不可能啊,when是怎么来的,我们回忆一下,是在sendMessageAtTime()里面,用当前时间加上delay的延迟时间,怎么会是0,除非故意传递0,很可能系统
//内部调用的,先不看
//3 when < p.when,那就是msg.when < mMessages.when,也就是说这个msg应该在队头执行之前执行的
//上面条件都说明:这个msg需要在队头之前执行,那是不是要插入到队头呢,看下面代码:
msg.next = p; //嗯,这个msg的next是p
mMessages = msg; //然后队头指向了这个msg,哦,明白了,就是说这个msg是第一个消息,就插到队头,ok了解
needWake = mBlocked; //如果当前阻塞了,需要唤醒,唤醒啥?后面再说,反正记住要唤醒就行
//**FLAG1** 看过MessageQueue.next()再回来看这里!!! 好,我们回来了,刚刚分析了一波如果没有消息需要处理就阻塞,现在这里明显有消息需要处理了,那么如果阻塞,就需要唤醒,
//所以needWake = mBlocked; 阻塞了就需要唤醒,否则不需要。好,去看FLAG2
} else {
//这里就是不需要插到队头的了
needWake = mBlocked && p.target == null && msg.isAsynchronous(); // 这个条件不用看了,系统使用的,就当false就行,以后分析
//此处往下是重点
Message prev;
for (; ; ) {
prev = p; //prev指向队头
p = p.next; //队头指向下一个
if (p == null || when < p.when) {
//1 p == null,除非是最后一个message了,也就是遍历完了
//2 when < p.when,那就是说这个msg在p之前执行了
break;
}
if (needWake && p.isAsynchronous()) { //这个先不用看了,系统使用的,就当false就行,以后分析
needWake = false;
}
}
msg.next = p; //遍历结束,msg在p之前执行,就将msg插入到p前面,所以msg.next = p;
prev.next = msg; //prev.next本来是p的,现在指向了msg,那就是将msg插入到prev和p之间了
//通过上面分析,可以看懂messageQueue是按照when的大小排序的,越早执行的越靠前
}
if (needWake) { //如果需要唤醒,就唤醒,目前我们看到的,只有在if里面才可能为true,也就是needWake = mBlocked,后面再分析mBlocked
nativeWake(mPtr);
//**FLAG2** ok我们回来了,如果next()里没有return msg;那么就会被阻塞,也就是会调用nativePollOnce()阻塞,
//那么此时有消息需要处理就使用nativeWake()来唤醒了,此时我们反复看enqueueMessage()这个函数和next()对比,就发现是成对的,一个存放msg,一个取出msg,
//然后通过Looper.loop()来检测,很好的机制
}
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
这里删除了部分代码,我们只看关键部分,仔细看注释,我们发现,整个流程就是将msg按照执行时间顺序插入到队列,越早执行的越靠前,如果msg是第一条消息(p == null), 或者需要立刻执行(when == 0||when < p.when),就会立刻唤醒,调用nativeWake(mPtr)去唤醒native执行,那么这个nativeWake(mPtr)是什么玩意呢,这里可以先简单的 理解为java层的notify()即可。那么我们把消息存入到MessageQueue里面了,从哪里取出来使用呢,我们回忆一下我们第一步的Looper的构造,是在ActivityTrhead.main() 函数里面,里面就两句:
public static void main(String[] args) {
...
省略若干代码
...
Looper.prepareMainLooper();
...
省略若干代码
...
Looper.loop();
这里有个异常,看来是跑不到,说明Looper.loop()是个死循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
2
3
4
5
6
7
8
9
10
11
12
13
我们已经知道第一句:Looper.prepareMainLooper()是创建了Looper,并且创建了MessageQueue,而我们刚刚的handler.sendMessage(message)也在这个messageQueue里面 添加了message,现在就差取出来message使用了,肯定在Looper.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;
boolean slowDeliveryDetected = false;
for (;;) { //这里果然是个死循环,否则上面就会抛出异常了
Message msg = queue.next(); //从messageQueue中取出message,终于找到了,在这里取出消息
if (msg == null) {
return; //没有消息就返回,就退出了循环,等等!那不就意味着上面那个异常会发生吗,app就crash了,除非msg==null不会发生,等会看看这个queue.next()会不会返回null
}
try {
//我们前面说过msg.target就是handler.sendMessage(msg)的这个handler,那就是调用了这个handler的dispatchMessage(msg),好我们看看
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
throw exception;
} finally {
***省略其他***
}
msg.recycleUnchecked(); //回收
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Handler.dispatchMessage()如下:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg); //嗯,如果message有callback就直接调用
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { //message没有callback就调用自己的mCallback了,也就是我们第一步的第三个参数
return;
}
}
handleMessage(msg); //最后才调用我们构造函数重载的handlerMessage(msg)方法,
}
}
2
3
4
5
6
7
8
9
10
11
12
上面逻辑很简单,这里总结一下:
- 1 如果msg有callback,那么只调用这个。
- 2 如果msg没有callback,先调用自己的mCallback(如果有的话),如果mCallback的handleMessage(msg)返回true,就结束,否则调用Handler自己的handleMessage(msg)。 分析完事,那么queue.next()会不会返回null呢,我们看看:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null; //这里有返回null!!! 卧槽,什么情况下发生?
}
int nextPollTimeoutMillis = 0; //记录下一条消息还有多长时间执行
for (;;) { //这里又是个死循环
...省略部分代码...
//这个可以理解为java的wait(),表示让native层wait(nextPollTimeoutMillis)时间,嗯,等待这么长时间
//还记得之前MessageQueue.enqueueMessage(msg)吗,里面的nativeWake(mPtr)就等价于noticy(),用来唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis(); //记录当前时间
Message prevMsg = null;
Message msg = mMessages; //取出第一条消息,也就是队头消息
//这个if不用看了,系统使用的,以后再讲
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//我们直接看这里
if (msg != null) { //msg!=null,也就是队头消息不为null,也就是队列非空
if (now < msg.when) {
//跑到这里表示当前时间小于第一条消息要执行的时间,也就是还没到执行的时候,msg是第一条消息,第一条消息还不到执行的时候,
//后面的消息都不用看了,因为前面分析过他们是按照执行时间从小到大排序的
//计算下一次执行还要多久,用msg.when - now算出来要等多久再取消息(这就是Handler能延时的原理),Integer.MAX_VALUE是为了防止应用层恶意传参数
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//跑到这里说明now >= msg.when,也就是说有消息要处理了
mBlocked = false; //那就解除阻塞
if (prevMsg != null) {
//这里不用看了,系统使用的,直接看else
prevMsg.next = msg.next;
} else {
//直接看这里,队头指向下一个消息
mMessages = msg.next;
}
msg.next = null; //这个msg的next赋值为null,那就是从队头删除了
msg.markInUse(); //标记为已使用
return msg; //返回,嗯,这个时候就回到looper.loop()里面了,去使用这个msg的targert.dispatchMessage(msg)了
}
} else {
//msg==null,那就是队列为null,直接赋值为-1,表示无限等待,那就只能等待enqueueMessage(msg)里面的nativeWake(mPtr)来唤醒了,
//也就是有消息才唤醒了
nextPollTimeoutMillis = -1;
}
mBlocked = true; //跑到这里,表示没有return msg;那么肯定是阻塞了,标记为true,好,现在去看FLAG1
...省略部分代码...
}
...省略处理空闲消息的代码...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
FLAG3,好现在我们从FLAG2回来了,我们总结一下: Looper.loop()是个死循环,从Message.next()获取msg,然后Message.next()也是死循环,循环取出当前队头的message,如果到了msg的执行时间就返回,并且使用mBlocked = false 标记为非阻塞,如果不到执行时间就先计算还有多久执行:int delay = msg.when - now, 然后调用nativePollOnce(delay)等待,等待结束就会执行,如果没有消息就无限等待,等待过程中会 把mBlocked = true标记为阻塞,等到有新消息入队,也就是enqueueMessage(msg,when)的时候,会判断此时是不是有消息需要执行: if (p == null || when == 0 || when < p.when), 如果有消息需要执行,就判断是否阻塞,如果阻塞,就唤醒(needWake = mBlocked),否则不唤醒。 本节分析到此结束,代码中标记的问题,后续会讲解。
问题1: MessageQueue不会无限休眠吗? 所以要么是限时等待,时间到了就会自动醒来,要么是无限等待,无限等待只发生在MessageQueue为空的情况下,而在此情况下,如果有新消息入队,一定满足p == null,也就是一定会唤醒,所以不存在 无限休眠的状态。
问题2: Handler怎么实现延时机制的? 仔细看注释的小伙伴已经知道了,Handler是获取当前时间,然后加上delay的时间,保存到message.when中,然后在looper.loop()中获取当前时间跟这个when对比,如果小于说明需要立刻执行,否则就做个减法: int waitTime = message.when - now;然后调用cpu等待waitTime再执行,从而达到了延时的效果。
问题3: 里面有一些异步消息是什么情况下使用的? 这个是用于屏幕渲染时候的VSYNC机制的,可以这么说: VSYNC通过handler来实现同步,VSYNC的消息都是异步的,会优先被处理,后面章节会仔细讲解。