[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)
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路