一、概述
如果要使用handler发送消息,每一个线程都应该初始化一个Looper对象,调用Looper对象的loop方法,循环的从MessageQueue中取得Message,然后通过Handler的dispatchMessage方法处理此消息,一般的,此方法的最终逻辑会执行我们实例化Handler对象时,重写的handleMessage方法里的逻辑代码。这里涉及到了消息机制的几个核心类:Handler,Message,MessageQueue,Looper。
二、源码分析
一般我们首先会实例化一个Handler的对象,所以先看Handler的构造方法:
public Handler(@Nullable 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());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
关键在这两句代码,mLooper = Looper.myLooper()拿到了looper的对象, mQueue = mLooper.mQueue拿到了MessageQueue的对象,然后先看Looper的myLooper方法:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
它是通过ThreadLocal的一个get存根获取到的Looper的对象,那么这个对象是在什么时候设置呢?我们知道每个线程都必须初始化一个Looper对象,不然会报错:Can't create handler inside thread that has not called Looper.prepare(),那么我们先看一下UI线程是怎么初始化Looper对象的,在ActivityThread里:
public static void main(String[] args) {
......
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
......
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们发现有一个 Looper.prepareMainLooper()的方法,它的核心逻辑如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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的prepare方法,在这个方法里,给ThreadLocal设置了一个Looper的对象,也就相当于把一个线程同一个Looper对象绑定在了一起,而通过Looper的构造方法我们知道了MessageQueue的对象是在初始化Looper对象的时候实例化的:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
需要说明的是,在主线程里API已经调用了Looper.prepare方法,但是在子线程里没有调用,所以我们需要自己手动调用Looper.prepare方法。接下来我们了解一下Looper到底是怎么从MessageQueue里拿消息的,通过ActivityThread的main方法,我们知道了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;
......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
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 {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
}
}
这是一个死循环,循环的从MessageQueue里取出消息,并通过 msg.target.dispatchMessage(msg)处理拿到的消息,这个msg.target其实就是handler对象,那么handler的对象是在什么时候赋值给message的target属性的呢?我们来看handler的sendMessage方法,也就是发送消息必须调用的方法,最终会调用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);
}
这里就是把自身的实例赋值给了message的target,然后通过MessageQueue的enqueueMessage方法,把此消息压入队列当中:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
......
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 {
......
Message prev;
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;
}
......
}
return true;
}
此处会按照 Message 的时间 when 来有序得插入 MessageQueue 中,可以看出 MessageQueue 实际上是一个有序队列,只不过是按照 Message 的执行时间来排序。至此,整个消息机制就已经基本分析完毕,下面讲一些面试相关的问题。
三、一些问题
首先是handler的post和sendMessage 的区别,post方法源码如下:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
这里可以看出,post方法把传入的Runnable 对象设置给了Message的callback,而在handler的dispatchMessage方法里,如果 Message 的 Callback 不为 null,一般为通过 post(Runnabl) 方式,会直接执行 Runnable 的 run 方法。因此这里的 Runnable 实际上就是一个回调接口,跟线程 Thread 没有任何关系。
其次就是Looper.loop() 为什么不会阻塞主线程,刚才我们了解了,Looper 中的 loop 方法实际上是一个死循环。但是我们的 UI 线程却并没有被阻塞,反而还能够进行各种手势操作,这是为什么呢?在 MessageQueue 的 next 方法中,有如下一段代码:
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
......
}
nativePollOnce是一个本地native方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制,我也不是很清楚这个机制的具体逻辑,有知道的同学可以补充一下。