Handler机制源码详解

671 阅读6分钟

Handler对于Android系统来说可以是非常重要的了,因为它贯穿了整个Android系统,它随处可见,没有Handler,Android系统也就不起作用了。

流程介绍

Android系统是以消息驱动的,整个流程可以大致概括为:

发送消息 --> 添加消息到队列 --> 从队列中获取消息 --> 处理消息

上面的流程可以引出几个类分别是,发送消息的Handler,将消息Message发送到消息队列MessageQueueLooper从消息队列循环取出消息,然后交给Handler处理。

在这里插入图片描述

  • Handler:消息发送和处理
  • Message:被发送和处理的消息
  • MessageQueue:存放消息的消息队列
  • Looper:循环的从MessageQueue中取消息给Handler处理

源码解析

发送消息

发送消息有sendpost的方式:

// 发送消息
Handler handelr = new Handler();
// 发送方式一
handler.sendMessage(msg); // 或者handler.sendEmptyMessage(what);

// 发送方式二
handler.postXXX(runnable);

handler.sendXXX()handler.postXXX()最终都会调用到sendMessageDelayed()方法。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
    	delayMillis = 0;
    }
    // 注意sendMessageAtTime方法的第二个参数,当前时间加上延时时间
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

然后调用了sendMessageAtTime方法

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); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}

又调用了enqueueMessage()方法

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
	msg.target = this;
	msg.workSourceUid = ThreadLocalWorkSource.getUid();

	if (mAsynchronous) {
		msg.setAsynchronous(true);
    }
	return queue.enqueueMessage(msg, uptimeMillis); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}

然后又调用了MessageQueueenqueueMessage()方法

boolean enqueueMessage(Message msg, long when) { // when = SystemClock.uptimeMillis() + delayMillis
	... // 省略无关代码
    synchronized (this) {
		...
            
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // 当消息队列为空或者将要入队的消息(msg)的时间(when)在所有消息队列的消息最前面,则把msg插入到队头,最先执行
            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;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    // 当p为空(遍历到最后一个了),或者新消息的时间在p的时间之前,就不用再遍历了,因为可以确定新消息要插在消息p前面了
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            
            // break跳出for循环后执行到这里,将msg插入到p前面,prev后面
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
		...
    }
    return true;
}

Message类的实现可以看出它是个单链表,消息队列中的消息都是按照时间先后顺序链接起来的。

接下来详细分析一下上述代码,在if分支中有三个条件判断p == null || when == 0 || when < p.when

  • p == null:当p也就是mMessages为空,表示消息队列中还没添加过消息
  • when == 0when表示当前时间SystemClock.uptimeMillis()加上延迟时间delayMillis,所以when == 0这个条件应该是一直返回false
  • when < p.when:新消息的时间when小于p消息的时间p.when

以上三个条件只要满足任一条件都会执行到if代码块中的语句,将新消息添加到p消息的前面,p消息为空时,即队头。

当添加新消息时,消息队列中是有消息的,就会执行到else语句中。先来看看下面代码是什么意思:

prev = p;
p = p.next;

第一行代码中的p就是在if代码块中添加的消息,说明指针指向p消息,第二行代码又将指针指向了p消息的下一个消息。没错,指针向后移动了一位。为什么要这样呢?

从上面2行代码中可以看出,再次添加消息时,遍历消息都是从添加if代码块中的消息开始的,因为从if代码块中添加的消息要么就是第一个消息,要么就是when小于p消息的wnen的,也就是说可确保前面的消息都是时间有序递增的。而系统的时间是一直在增加的,所以enqueueMessage()方法的第二个参数when一直大于mMessageswhen,所以要把指针向后移一位,从后面一个消息开始比对。

上图加深理解(图中所有msgwhen为了偏于比较和理解,都是用的比较小的数字,而没有使用时间戳):

在这里插入图片描述

添加第一个消息时,消息队列中是没有消息的,直接将消息msg1添加到消息队列的头部。

在这里插入图片描述

当添加第二个消息时,消息队列中已经有消息msg1,此时不满足if的条件,执行到else中的代码,因为p = mMessages,而mMessages在添加第一个消息时被赋值成了msg1,代码p = p.next将指针指向了msg1的下一个,也就是null,即p = null。当p == null时会跳出循环,会执行到

msg.next = p
prev.next = msg

也就是将msg2的下一个节点设置成p,即将msg2消息插入到p的前面。如下图: 在这里插入图片描述

上面添加的两个消息不是插入到队头就是队尾,再插入一个到中间的消息看看代码是怎么执行的。 在这里插入图片描述

因为mMessage只在添加msg1的时候被赋值了,所以p = mMessage = msg1,接下来执行if的判断条件if (p == null || when == 0 || when < p.when)p = msgwhen = 4p.when = 1,不满足if条件,执行到else中的代码。

再来看else中的for循环,p = p.next将指针移到了msg2的位置,此时p = msg2when = 4p.when = 5,满足了for循环的if条件when < p.when

for (;;) {
    prev = p;
    p = p.next;
    if (p == null || when < p.when) { // 满足了该条件,跳出循环
    	break;
    }
    if (needWake && p.isAsynchronous()) {
    	needWake = false;
    }
}

跳出循环后,执行代码

msg.next = p; // invariant: p == prev.next
prev.next = msg;

msg3插入到msg2前面,至此msg3的插入流程就走完了。

突然发现,好像除了添加msg1的时候执行到了if中的代码,后面都是执行else的代码。那在什么情况下会再次执行if中的代码呢?从if的条件中可以看出,此时p = msg1不会为空,when实际上是当前时间的时间戳,也不会等于0,所以只有当when < p.when的时候才会再次执行到if中的代码。

那再次发送一个消息msg4,假设msg.when = 0(实际上不会为0,这里为了方便假设为0,这个0不等于if条件中的0),满足条件后执行

msg.next = p;
mMessages = msg;

msg4插入到p的前面,也就是msg1的前面了,然后又刷新了mMessages的值了。 在这里插入图片描述

总结
  • 添加第一个消息时,将消息添加到消息队列的队头
  • 再次添加消息时,先判断待添加消息的whenp.when的大小关系,小于p.when,则把消息添加到p的前面,否则循环消息队列找出合适的插入位置
  • 如果遍历完整个消息队列都没有满足条件的位置,则把新消息插入到队尾
  • 消息队列的消息按照时间从先到后排序

取出消息

开头说过整个系统是以消息驱动的,那么Looper就是其动力了,Looper.loop()方法不断的从MessageQueue中取消息。那么Looper.loop()方法是在哪里调用的呢?由于系统都是由消息驱动的,所以在系统启动的时候就应该有动力驱动了,在SystemServer.main()中调用了Looper.loop(),这是在系统层面的驱动,而对于APP应用层面来说,应用入口ActivityThread.main()中也需要调用Looper.loop(),毕竟APP也是需要各种事件响应的嘛。来看看loop()的代码:

public static void loop() {
	final Looper me = myLooper(); // 先获取Looper对象
	if (me == null) {
		throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
	}
	final MessageQueue queue = me.mQueue; // 获取消息队列,获取到了消息队列后才能从消息队列中取出消息

	... // 省略无关代码

	for (;;) {
		Message msg = queue.next(); // 获取消息,无消息时可能会阻塞
		if (msg == null) {
			// No message indicates that the message queue is quitting.
			return;
		}

		... // 省略无关代码
            
		// Make sure the observer won't change while processing a transaction.
		final Observer observer = sObserver;

		... // 省略无关代码

		Object token = null;
		if (observer != null) {
			token = observer.messageDispatchStarting(); // 开始分发消息
		}

		try {
			msg.target.dispatchMessage(msg); // 分发处理消息
			if (observer != null) {
				observer.messageDispatched(token, msg); // 消息分发处理完成
			}
			dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
		} catch (Exception exception) {
			if (observer != null) {
				observer.dispatchingThrewException(token, msg, exception); // 消息分发异常
			}
			throw exception;
		} finally {
		}
		... // 省略无关代码
		msg.recycleUnchecked(); // 回收消息
	}
}

首先要获取到Looper对象,当Looper对象为空时抛出异常No Looper; Looper.prepare() wasn't called on this thread.,我们就知道调用loop()方法之前需要先调用prepare()方法,在prepare()中创建出了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));
}
private Looper(boolean quitAllowed) {
	mQueue = new MessageQueue(quitAllowed);
	mThread = Thread.currentThread();
}

在创建Looper时创建了MessageQueuesThreadLocal保证了一个线程只有一个Looper,而一个Looper又只有一个MessageQueue

插入话题来看看ThreadLocal是怎么做到这些的。

回头看看prepare()方法,首先调用sThreadLocal.get()获取Looper,如果不为空说明已经创建过Looper了,为空就创建一个Looper保存在sThreadLocal中,看看sThreadLocal.get()的实现:

// 获取泛型T,这里是Looper
public T get() {
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	if (map != null) {
		ThreadLocalMap.Entry e = map.getEntry(this);
		if (e != null) {
			@SuppressWarnings("unchecked")
			T result = (T)e.value;
			return result;
		}
	}
	return setInitialValue();
}

// 获取当前线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
	return t.threadLocals;
}

// 当ThreadLocalMap为空时,设置初始值
private T setInitialValue() {
	T value = initialValue();
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	if (map != null)
		map.set(this, value);
	else
		createMap(t, value);
	return value;
}

protected T initialValue() {
    return null;
}

// 创建ThreadLocalMap
void createMap(Thread t, T firstValue) {
	t.threadLocals = new ThreadLocalMap(this, firstValue);
}

获取到当前线程,然后根据当前线程获取ThreadLocalMapThreadLocalMapThread的一个变量,表明一个线程只有一个ThreadLocalMap。接着判断ThreadLocalMap是否是空,为空则创建一个,否则就获取ThreadLocalMap中的值。再来看看sThreadLocal.set(T)方法:

# ThreadLocal.java
public void set(T value) {
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	if (map != null)
		map.set(this, value); // this就是sThreadLocal,static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
	else
		createMap(t, value);
}

set()方法中先是获取了当前线程,然后根据当前线程获取ThreadLocalMap,当map为空时就根据当前线程创建一个,否则就将value设置到map中。从map.set(this, value)可以看出,是用当前ThreadLocal对象sThreadLocal作为keysThreadLocal是所有线程共享的,一个线程只有一个ThreadLocalMap,而一个ThreadLocalMapkey = this时只能获取到一个value

再回头看看Looper.prepare()方法中的sThreadLocal.get()方法,从sThreadLocal.set()方法中我们已经知道了Looper和线程是一一对应的关系,从当前线程中获取的Looper不为空时就抛出异常,这样就避免了Looper再次创建。 在这里插入图片描述

话题转回来,获取到了looper对象后,就从looper中获取MessageQueue对象queue,然后在for(,,)循环中调用queue.next取消息,当消息队列中没有消息时,就会阻塞在queue.next()这里。来看看queue.next()中做了什么:

Message next() {
	... // 省略无关代码
        
	int nextPollTimeoutMillis = 0;
	for (;;) {
		if (nextPollTimeoutMillis != 0) {
			Binder.flushPendingCommands();
		}

		nativePollOnce(ptr, nextPollTimeoutMillis); // 休眠

		synchronized (this) {
			// Try to retrieve the next message.  Return if found.
			final long now = SystemClock.uptimeMillis();
			Message prevMsg = null; // 正常同步消息用不到这个
			Message msg = mMessages;
            // 当msg,target == null,表示这是同步屏障,msg就是优先执行的消息。
			if (msg != null && msg.target == null) {
				// Stalled by a barrier.  Find the next asynchronous message in the queue.
				do {
					prevMsg = msg;
					msg = msg.next; // 将同步屏障的异步消息放在队头,优先执行
				} while (msg != null && !msg.isAsynchronous());
			}
            
            // 主要看下面的代码
			if (msg != null) {
                // 代码片段1
				if (now < msg.when) {
					// Next message is not ready.  Set a timeout to wake up when it is ready.
                    // 当前时间大于消息的时间,说明还没有到该消息执行的时候,计算出消息执行的延时时间
					nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
				} else {
                    // 代码片段2
					// Got a message.
					mBlocked = false;
					if (prevMsg != null) {
						prevMsg.next = msg.next;
					} else {
						mMessages = msg.next; // msg将要进行处理,然后会将处理的消息删除,所以消息指针需要向后移一位
					}
					msg.next = null; // 表示将msg从消息队列中剔除
					if (DEBUG) Log.v(TAG, "Returning message: " + msg);
					msg.markInUse();
					return msg;
				}
			} else {
				// No more messages.
				nextPollTimeoutMillis = -1; // 没有消息,继续休眠等待
			}

			... // 省略无关代码
		}

		... // 省略无关代码
	}
}

next()里面是一个死循环,enqueueMessage()next()方法中都用了同步锁,避免在发送消息时取消息以及取消息时发送消息,保证了线程安全。主要看synchronized(this)中的代码,首先获取到msg,就拿下图中发送的消息来看,此时的msg= mMessages = msg4msg不为空,执行到代码片段1处,然后比较当前时间和msg.when的大小,如果now < msg.when,表明还没有到执行该消息的时候,计算出延迟执行时间;否则就执行代码片段2prevMsg为空,所以执行mMessages = msg.next,将指针后移一位,即下图中的msg1处,然后执行msg.next = null,将msg4消息剔除。 在这里插入图片描述

这里要注意的是msg.next = null并不是将msg1设置为空的意思,而是将msg4与后面的消息节点断开连接。看下图 在这里插入图片描述

获取到消息之后会将消息返回,然后又回到了Looper.loop()方法的for(;;)中,返回的消息就是Message msg = queue.next();中的msg,然后将msg分发给handler处理,再次看上面的loop()方法,下面贴出关键代码:

try {
	msg.target.dispatchMessage(msg); // 分发处理消息
	if (observer != null) {
		observer.messageDispatched(token, msg); // 消息分发处理完成
	}
} catch (Exception exception) {
	...
} finally {
    ...
}

调用到msg.target.dispatchMessage(msg)方法,那msg.hander是什么呢?其实它就是Handler,还记得handler.enqueueMessage()方法吗?

#Handler.java
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
		long uptimeMillis) {
	msg.target = this; // 在这里设置了target
	msg.workSourceUid = ThreadLocalWorkSource.getUid();

	if (mAsynchronous) {
		msg.setAsynchronous(true);
	}
	return queue.enqueueMessage(msg, uptimeMillis);
}
总结
  • 必须要先调用Looper.prepare()之后再调用Looper.loop()
  • 消息队列中没有消息的时候会堵塞在next()方法处让CPU休眠,不会ANR
  • 取到了消息后要判断当前时间和消息执行直接的先后关系,没到执行时间则继续休眠等待,否则就开始处理消息
  • 处理消息前,会将该消息从消息队列中剔除
  • 最后调用到handleMessage()方法处理消息(这条放在这里一起总结了)

处理消息

Handler类中发送消息的时候就设置了msg.targetthis指的就是当前的Handler对象。再次回到了Handler中,看看dispatchMessage做了什么事情。

public void dispatchMessage(@NonNull Message msg) {
	if (msg.callback != null) {
		handleCallback(msg);
	} else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) {
				return;
			}
		}
		handleMessage(msg);
	}
}

msg.callback是在创建Message的时候赋值的,而mCallback是在构造Handler的时候赋值的

public static Message obtain(Handler h, Runnable callback) {
	Message m = obtain();
	m.target = h;
	m.callback = callback; // 给msg.callback赋值

	return m;
}
public Handler() {
	this(null, false); // 默认callback为空
}

public Handler(@Nullable Callback callback, boolean async) {
	... // 省略无关代码
	mQueue = mLooper.mQueue;
	mCallback = callback; // 给mCallback赋值,默认为空
	mAsynchronous = async;
}

因此msg.callbackmCallback都为空,接下来执行到handleMessage(msg)方法

public void handleMessage(@NonNull Message msg) {
}

handleMessage方法是一个空实现方法,具体的实现在Handler的回调中

Handler handler = new Handler() {
	@Override
	public void handleMessage(@NonNull Message msg) {
		super.handleMessage(msg);
        // 实现具体的逻辑
	}
};

扩展

dispatchMessage()中处理消息的地方有三处,默认调用的就是handleMessage(msg),那什么时候调用其它2种方式呢?

  • handleCallback(msg)

msg.callback不为空就会执行handleCallback(msg),那么就需要先给msg.callback赋值了。上面说过msg.callback是在创建Message的时候赋值的,创建Message是通过Message.obtain()来创建的,来看看obtain方法的重载方法:

public static Message obtain(){}
public static Message obtain(Message orig){}
public static Message obtain(Handler h){}
public static Message obtain(Handler h, Runnable callback){}
public static Message obtain(Handler h, int what){}
public static Message obtain(Handler h, int what, Object obj){}
public static Message obtain(Handler h, int what, int arg1, int arg2){}
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj){}

obtain有8个重载方法,第四个重载方法可以设置msg.callback,那在创建Message的时候可以这样写:

Handler handler = new Handler();
Message message = Message.obtain(handler, new Runnable() {
	@Override
	public void run() {
	}
});
handler.sendMessage(message); // 发送消息

Message obtain(handler, callback)中对msg.callback进行了赋值

public static Message obtain(Handler h, Runnable callback) {
	Message m = obtain();
	m.target = h;
	m.callback = callback; // 赋值
	return m;
}

然后在分发消息时会执行到handleCallback(msg)

private static void handleCallback(Message message) {
	message.callback.run();
}

最终会回调到创建Message时传入的第二个参数Runnablerun方法中。

  • mCallback.handleMessage(msg)

msg.callback的优先级最高,其次是mCallback,只有在msg.callback为空时才会执行到mCallback.handleMessage(msg),所以在创建Message的时候不能再传入callback了。mCallback是在构造Handler的时候赋值的,所以要在创建Handler的时候就要传入mCallback。来看看Handler的构造函数:

public Handler(){}
public Handler(@NonNull Looper looper){}
public Handler(@NonNull Looper looper, @Nullable Callback callback){}
public Handler(boolean async){}
public Handler(@Nullable Callback callback){}
public Handler(@Nullable Callback callback, boolean async){} // @hide
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async){}

`

Handler有7个构造函数,我们可以通过最简单的第五个构造函数来创建Handler

Handler handler = new Handler(new Handler.Callback() {
	@Override
	public boolean handleMessage(@NonNull Message msg) {
		return false;
	}
});
Message message = Message.obtain(handler);
handler.sendMessage(message);

Handler的构造函数中对mCallback进行了赋值,最终会调用到第六个被标上@hide的构造函数:

public Handler(@Nullable Callback callback, boolean async) {
	... // 省略无关代码
	mQueue = mLooper.mQueue;
	mCallback = callback; // 赋值
	mAsynchronous = async;
}

此时,msg.callback为空,mCallback不为空,就会调用mCallback.handleMessage(msg)方法,最后回调到了创建Handler的参数Handler.CallbackhandleMessage(msg)中。