源代码解读
- 开启线程HandlerThread首先是run方法跑起来,run方法里主要做两件事情,一个是创建Looper,一个是循环Looper
public class HandlerThread extends Thread {@Overridepublic void run() {Looper.prepare(); //创建LoopermLooper = Looper.myLooper(); // 然后获取Looper,也就是线程持有Looper。业务代码创建handler的时候调用getLooper()就是得到的这个LooperLooper.loop(); //启动循环Looper}
}
- 看看Looper.prepare()是怎么样给当前线程创建唯一的Looper对象的?
public final class Looper {// Looper类有个static的全局变量sThreadLocal,也就是说所有Looper对象共享这个全局变量sThreadLocal。这个变量用法相当于一个Map,它的key是当前线程Thread,键值是Looper。static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();private static void prepare(boolean quitAllowed) {// 判断当前线程是否已经有Looper了?如果不为空就有有了,那就直接报错,就是这里确保了一个线程只能有一个Looper。// 面试的时候人家问你一个线程最多有几个Looper知道怎么回答了吧?(回答:一个线程对应一个Looper,通过Looper类的全局变量sThreadLocal确保实现的)if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}// 创建new Looper()放在全局变量存着sThreadLocal.set(new Looper(quitAllowed));}// 获取Looper对象自然是从这个全局变量直接拿了public static @Nullable Looper myLooper() {return sThreadLocal.get();}//Looper构造函数直接创建了一个消息队列MessageQueue。那就是一个Looper对应一个消息队列了,面试的时候懂了吧?//那一个线程对应多少个消息队列呢?回答:肯定是一个啊,上面不是说了一个线程只有一个Looper了吗?那一个Looper就一个消息列表,所以一个线程就只有一个消息队列!private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed); // Looper支持MessageQueue消息对应哦,上面备注说了线程持有Looper,那就是线程持间接有MessageQueue了(thread.mLooper.mQueue嘛,这个对后面handler消息派发流程的理解有用哦)mThread = Thread.currentThread();}
}
- 看看Looper.loop()干了啥,死循环为什么主线程卡住?
public final class Looper {//for (;;) {} 拿到队列后进去for死循环public static void loop() {final Looper me = myLooper(); //获取消息队列for (;;) { //死循环消息队列,看到了吧?为什么主线程不会卡住呢?继续往下看,使用了linux的epoll-wait机制实现if (!loopOnce(me, ident, thresholdOverride)) {return;}}}//private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // might block,队列的next方法里使用了linux的epoll-wait机制实现,如果没有消息的时候会进去等待状态,直到超时或者调用了nativeWake(mPtr)方法msg.target.dispatchMessage(msg); //这里的target就是hanlder,看后面的分析就知道}
}
public final class MessageQueue {Message next() {int nextPollTimeoutMillis = 0;//一开始为0,nativePollOnce第一次调用后立即返回for (;;) {//底层使用epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);实现nativePollOnce(ptr, nextPollTimeoutMillis);final long now = SystemClock.uptimeMillis();Message msg = mMessages; //mMessages是消息队列头指针if (msg != null && msg.target == null) { //如果target为空说明是barrier屏障消息do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous()); //if条件target为空遇到了屏障消息后,找到最新异步消息。//这里可能有个面试题:怎么保证UI刷新消息优先执行?回答:通过屏障消息+异步消息实现,确保异步消息优先执行。}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); //next消息时间还没到的话,立即下一次poll_wait等待时间}else{msg.markInUse();return msg;}} }
}
- 一个线程(一个Looper)对应几个Handler?回答:这里就不是只能对应一个了哦,可以多个,你创建了几个就对应几个啊
//创建了几个就对应几个啊,但是你发现没有,每次创建Handler都需要自己Looper的。这个Looper.getMainLooper()可是同一个Looper哦。也就是多个Handler可以共用一个Looper(也共用同一个HandlerThread,共用同一个MessageQueue)。
Handler handler1 = new Handler(Looper.getMainLooper());
Handler handler2 = new Handler(Looper.getMainLooper());
Handler handler3 = new Handler(Looper.getMainLooper());
//Handler内部持有mLooper和mQueue哦
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async, boolean shared) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;mIsShared = shared;
}
//handler.post一个消息,最终都会调用
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) { //指定时间为负数也没用,会帮你修复为0delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); //SystemClock.uptimeMillis()是手机开机时间哦,不是系统时间。所以为什么修改了系统时间还能确定消息的顺序
}public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {MessageQueue queue = mQueue;return enqueueMessage(queue, msg, uptimeMillis);
}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {msg.target = this; //看到了吧?target就是this哦,这个this就是handler对象哦。注意:同步消息和异步消息都有target,但是屏障消息是没有target的。msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis); //最后调用了队列的enqueueMessage方法将消息添加到队列,等待Looper的消费了。所以为什么Handler构造函数里通过Looper获取了mQueue知道了吧。
}
handler流程图

总结概括
- 一个HandlerThread线程只有一个Looper,一个Looper只有一个MessageQueue。Looper通过static类型的sThreadLocal确保每个HandlerThread线程仅有一个Looper。
- Handler是可以有多个的,不是只能有一个哦,但是创建Handler的时候需要Looper作为参数,传递的是哪个Looper,最后消息就是进去哪个Looper对应的消息队列。