ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] 一句话总结:Looper 不断从 MessageQueue 中取出一个 Message,然后交给其对应的 Handler 处理。<!--more--> 先上一幅整体关系图,来自郭神 ![](https://ws4.sinaimg.cn/large/006tKfTcgy1fkuqqucfpwj30j40dymy2.jpg) # Handler使用 Handler在Android中主要用于线程间的通信,经典使用方法如下: ```java Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { //... } } }; Message message = Message.obtain(); message.what = 1; mHandler.sendMessage(message); ``` 下面我们通过源码来一起看看Handler的真面貌。 # Handler源码分析 Handler类在整个机制中所做的事情只有两件,一个是发布新消息,一个是处理消息,我们分别来看看。 ## Handler发布消息 Handler的sendMessage()方法源码如下: ```java public final boolean sendMessage(Message msg){ // 默认延迟时间为0ms return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 设置Message的target为Handler自己,target是Message类的一个成员变量,为Handler类型的 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } ``` 代码并不复杂,最终调用了MessageQueue的enqueueMessage方法将方法发送出去。enqueueMessage源码如下: ```java boolean enqueueMessage(Message msg, long when) { // 检查Message的target不能为空,因为后面Looper取出消息后,交由这个target(Handler)处理的 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { msg.markInUse(); msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { // 将Message置于消息链表的表头位置 msg.next = p; mMessages = msg; } else { Message prev; // 死循环遍历Message链表 for (;;) { prev = p; p = p.next; // 如果当前Message的延迟执行时间比所对比的Message执行时间小,就插到它前面 // 不然继续循环,和多对比Message的next所指向的Message继续对比 if (p == null || when < p.when) { break; } } // 将当前Message插入消息链表 msg.next = p; prev.next = msg; } } return true; } ``` ![](https://img.kancloud.cn/0b/4e/0b4ef3f3318a9fb973b648dbbfb67033_834x141.png) Message插入逻辑在注释中写的也很清楚了,注意两点: * MessageQueue采用单链表结构,表头的Message是会最先被取出执行的 * 通过死循环遍历链表,对比执行延迟时间,来决定将Message插入到何处 消息已经插入到消息队列中了,下面我们看看消息又是怎么取出来的。 ## Handler处理消息 Handler除了插入消息到消息队列中,还承担着处理消息的职责。先从我们重写的handleMessage方法作为入口。 ```java public void handleMessage(Message msg) { } ``` handleMessage是Handler类的一个空方法,需要子类来实现,我们看看这个方法是在哪里被调用的。 ```java public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ``` dispatchMessage方法是由Looper调用的,Looper在整个Handler机制中的作用是从MessageQueue中不断取出Message,交由Handler处理,Handler调用dispatchMessage方法进行Message的处理,Looper的源码我们稍后分析。 Handler在处理消息时分为三种情况: ```java if (msg.callback != null) { handleCallback(msg); } private static void handleCallback(Message message) { message.callback.run(); } ``` 1、先判断Message的callback是否为空,不为空直接调用其run方法,不再往下走 2、判断Handler的mCallback是否为空,不为空直接调用其handleMessage方法,根据返回值决定是否往下走 3、如果Message和Handler的Callback均为空,调用Handler的handleMessage方法 其中,Message的callback在其obtain时进行设置,我们比较少手动调用带参数的obtain方法,暂不关心;Handler的Callback则是在Handler构造的时候传入,我们可以手动传入;Handler的handleMessage方法是我们手动重写的,用于处理Message。因此,我们可以通过在构造Handler时传入Callback来处理消息,或者直接重写Handler的handleMessage方法来处理消息。 补充刚刚Handler构造Callback的源码: ```java public Handler(Callback callback) { this(callback, false); } public Handler(Callback callback, boolean async) { //... mCallback = callback; mAsynchronous = async; } ``` 刚刚提到Looper不断从MessageQueue中提取消息,然后给Handler进行处理,下面我们来看看Looper: ## Looper提取消息 ### Looper的创建 Handler类有一个成员变量mLooper: ```java final Looper mLooper; ``` mLooper的初始化有两种方式,分别如下: ```java /** * @param looper: The looper, must not be null */ public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } ``` 可以看到,mLooper一定不能为null。来看看Looper的myLooper()方法,看看如何从当前线程获取Looper: ```java static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ``` 此处引出了ThreadLocal的概念,稍后会详细分析。暂时可以理解为,Looper对象是当前线程的一个局部变量,其他线程无法访问及修改, 上面是获取Looper的方法,下面看看为当前线程设置Looper的方法,操作是在 Looper的prepare()方法完成的,看源码: ```java 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对象是和线程绑定的,一个线程有且只能有一个Looper对象。使用Handler机制来做线程间通信时,要保证该线程的Looper不为空,也就是当我们为一个子线程开启Handler机制时,需要先调用Looper.prepare()方法来创建Looper对象,后面Looper才可以从消息队列中取出Message。 下面来看看Looper的构造方法: ```java private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } ``` 可以看到,Looper有两个成员变量,一个是消息队列,一个是当前线程。Looper是如何从消息队列取消息的呢, 来看看: ```java public static void loop() { // 从ThreadLocal中取出Looper 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(); if (msg == null) { // 没有消息表明消息队列正在退出 return; } //... try { msg.target.dispatchMessage(msg); } //... } } // 前面我们在Looper.prepare()方法中,在ThreadLocal中存入新创建的Looper对象。现在取出使用 public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ``` 逻辑很清晰,先从ThreadLocal中取出我们创建的Looper对象,从Looper中拿到消息队列。然后一个for死循环,从消息队列中循环取出消息,调用msg.target的dispatchMessage方法,msg.target就是Handler对象,也就是将消息交由Handler处理。 ### 拓展:ThreadLocal 1、ThreadLocal是一个创建线程局部变量的类。 2、一般情况下,我们创建的变量可以被任何一个线程访问并修改,而使用ThreadLocal创建的变量只能被当前线程访问,其他线程无法访问和修改。 ThreadLocal使用方法: ```java // 创建 ThreadLocal<String> mStringThreadLocal = new ThreadLocal<>(); // set方法 mStringThreadLocal.set("Canton"); // get方法 mStringThreadLocal.get(); // 设置默认初始值 ThreadLocal<String> mNewStringThreadLocal = new ThreadLocal<String>() { @Override protected String initialValue() { return "A new threadLocal"; } } ``` ThreadLocal原理: 先从ThreadLocal的set方法看看ThreadLocal是如何存储的: ```java public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { // Thread类的threadLocals声明如下: // ThreadLocal.ThreadLocalMap threadLocals = null; // 是一个ThreadLocalMap return t.threadLocals; } // 一个自定义的HashMap,用来存放线程局部变量值 static class ThreadLocalMap { //... } // 创建存放线程局部变量值的HashMap void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ``` ThreadLocal的get方法源码如下: ```java public T get() { // 获取当前线程 Thread t = Thread.currentThread(); // 获取当前线程的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); T result = (T)e.value; return result; } return setInitialValue(); } ``` 可以看到 a、ThreadLocal的set方法存入的值,实际是放到当前线程的ThreadLocalMap中,key为当前ThreadLocal对象,value为存入的对象。 b、get方法直接从当前线程的ThreadLocalMap中,根据key获取对象。 c、注意,ThreadLocal的值并不是存放在ThreadLocal中的! ## 发送消息的其他方法 除了使用Handler的sendMessage方法,还有其他几种方法可以发送Message到消息队列中,总结如下: 1、使用Handler的post方法发送消息: ```java public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } ``` 可以看到,和Handler的sendMessage方法一样,还是调用了Handler的sendMessageDelayed方法。区别在于,调用了getPostMessage方法为Message设置了Callback。前面处理消息时,我们分析过,如果Message有设置Callback时,直接回调Message的Callback的run方法,不再执行Handler的Callback或者Handler的handleMessage方法,这点需要注意。 2、使用View的post方法发送消息: ```java public boolean post(Runnable action) { // 当一个View添加到Window时,会在AttatchInfo中存储一些信息,其中就有UI线程的Handler final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // 调用Handler的post方法 return attachInfo.mHandler.post(action); } // 如果View还没被添加到一个Window中,就先将Runnable存到一个队列中,等View添加到Window并且有Handler了再进行执行 getRunQueue().post(action); return true; } ``` 3、使用Activity的runOnUiThread方法发送消息 ```java public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { // 当前线程不是UI线程,调用UI线程的Handler将Runnable包装成Message发送出去 mHandler.post(action); } else { // 当前线程就是UI线程,直接执行即可 action.run(); } } ``` ## 创建Handler的几种重载 ```java // 调用两个参数的构造方法 public Handler() { this(null, false); } // 调用两个参数的构造方法 public Handler(Callback callback) { this(callback, false); } // 调用两个参数的构造方法 public Handler(boolean async) { this(null, async); } // 调用三个参数的构造方法 public Handler(Looper looper) { this(looper, null, false); } // 调用三个参数的构造方法 public Handler(Looper looper, Callback callback) { this(looper, callback, false); } ``` 两个参数和三个参数的构造方法分别如下: ```java // 两个参数的构造方法 public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public static @Nullable Looper myLooper() { return sThreadLocal.get(); } // 三个参数的构造方法 public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } ``` 可以看到,Handler的构造方法重载虽然多,但最终要么调用了两个参数的构造方法,要么调用了三个参数的构造方法。其中,三个参数的构造方法直接设置了Handler的Looper、Callback、消息是同步还是异步;两个参数的少了一个Looper参数,直接调用了Looper.myLooper方法,来从ThreadLocal中获取当前线程的Looper。 因此,在UI线程调用两个参数的构造方法时,会自动获取UI线程的Looper,并设置给Handler;但是直接在子线程调用两个参数的构造方法时,由于还没调用Looper.prepare(),因此子线程的Looper为空,所以会直接抛出异常。 注意! Android 中除了 UI 线程(主线程),创建的工作线程默认是没有消息循环和消息队列的。如果想让该线程具有消息队列和消息循环,并具有消息处理机制,就需要在线程中首先调用 Looper.prepare()来创建消息队列,然后调用 Looper.loop()开启消息循环。 ## HandlerThread 关于 HandlerThread 的使用,源码中是这样说的,使用 HandlerThread 可以很方便的开启一个包含 Looper 的线程,开启线程后,可以使用和该线程绑定的 Looper 去构建相应的 Handler。并且可以通过 HandlerThread 的 quit 和 quitSafely 方法很方便的终止线程的消息循环队列。 HandlerThread 使用示例: ```java HandlerThread handlerThread = new HandlerThread("test"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //... } }; handlerThread.quit(); handlerThread.quitSafely(); ``` 其中HandlerThread的源码大概如下: ```java public class HandlerThread extends Thread { @Override public void run() { Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); } onLooperPrepared(); Looper.loop(); //... } } public Looper getLooper() { //... return mLooper; } ``` 可以看到,HandlerThread继承自Thread,在run方法中,调用Looper的prepare方法创建Looper对象并和HandlerThread进行绑定,最后调用Looper的loop方法开启循环提取消息。我们在创建Handler时,直接将和HandleThread绑定在一起的Looper传递给Handler即可。这样子Handler就会分发以及处理HandlerThread上的消息。 # 整体流程总结 1、Handler调用sendMessage、post方法发送Message,并插入到MessageQueue中,MessaQueue采用单链表结构。 2、Handler类有一个Looper成员变量,Looper属于线程局部变量,每个线程有且只能有一个Looper;Looper类有一个MessageQueue成员变量;Handler持有Looper主要是为了拿到Looper对应的MessageQueue,并往其中插入消息。 3、子线程需要先调用Looper.prepare方法,来创建一个Looper对象存储到ThreadLocal中。然后创建Handler时会调用Looper.myLooper方法获取当前线程的Looper。 4、Looper.loop方法开启消息循环,Looper会循环从其MessageQueue中提取消息,并调用消息的target(也就是Handler)进行分发处理 5、Handler拿到Message后,先判断Message的Callback是否为空,不为空直接执行,消息处理结束;为空则判断Handler的Callback是否为空,不为空则执行,并决定是否进行拦截,拦截则消息处理结束;不拦截则执行Handler的handleMessage方法。 参考文档: [深入理解 Android 内核设计思想](https://book.douban.com/subject/25921329/) [Android消息机制(一):概述设计架构](http://www.jianshu.com/p/8656bebc27cb) [Android消息队列模型](http://superonion.iteye.com/blog/1442416) [Android 多线程编程的总结](https://www.diycode.cc/topics/213)