在Android开发中,Handler作为实现线程间通信的桥梁,扮演着至关重要的角色。无论是在主线程执行UI操作,还是在子线程进行耗时任务,Handler都可以高效地将异步消息分派到对应的线程中执行。
本文将全方位解析Handler的工作原理及实现细节,从源码角度介绍Looper , Handler , Message的关系,让你记忆深刻。
一、Handler原理浅析
Handler实际是Android低层面向线程的消息循环机制MessageQueue的一层包装。
1、两个关键组成部分:
- MessageQueue消息队列 - 用于存放所有通过Handler发送的消息
- Looper消息循环器 - 负责不断从MessageQueue中取出消息,并按序执行
每个线程都可以通过Looper.prepare()方法创建自己的消息循环,并在循环体内通过Looper.loop()不断获取并执行消息。
Android 中的 Handler 机制是用于在不同线程之间进行通信和消息传递的重要机制。
2、工作流程如下
(1)、创建 Handler 实例,并重写 handleMessage()
方法,用于处理接收到的消息。
(2)、发送消息: 通过 Handler
的 sendMessage()
等方法发送消息到消息队列。
(3)、消息循环: 消息队列会不断地从队列中取出消息,并分发给对应的 Handler
处理。
我们可以通过一个简单的示例来理解它的基本工作流程:
java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建 Handler 对象
handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理收到的消息
switch (msg.what) {
case 0:
Log.d(TAG, "Received message: " + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
// 在子线程中发送消息
new Thread(new Runnable() {
@Override
public void run() {
// 创建消息对象
Message message = Message.obtain();
message.what = 0;
message.obj = "Hello from worker thread!";
// 发送消息
handler.sendMessage(message);
}
}).start();
}
}
在这个示例中,我们做了以下操作:
- 在
onCreate
方法中,我们创建了一个Handler
对象,并重写了handleMessage
方法。这个方法会在收到消息时被调用,我们在这里处理收到的消息。 - 我们创建了一个子线程,在这个线程中创建了一个
Message
对象,并通过handler.sendMessage()
方法将其发送给 Handler。 - 当 Handler 收到消息时,它会将消息添加到消息队列中,然后等待 Looper 取出并处理这个消息。Looper 会调用我们重写的
handleMessage
方法来处理这个消息。
实际上,Handler 的实现机制要复杂得多,涉及到 MessageQueue、Looper 等多个组件的协作。但是理解了这个基本示例,就可以对 Handler 的工作原理有一个基本的认知了。
二、Handler 源码解析
下面我们来深入解析 Handler 的源码实现。
1、 Handler 使用回顾
我们先来回顾下Handler 使用流程
// 在主线程中创建 Handler 来处理子线程发送的消息
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
//TODO: 处理消息
break;
}
}
};
// 使用方式一:在子线程中发送消息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 0;
message.obj = "测试消息";
// 子线程中发送消息
handler.sendMessage(message);
}
}).start();
// 使用方式二:handler.post()
handler.post(new Runnable() {
@Override
public void run() {
// 运行在子线程中...
}
});
通过上面代码可以看到,在使用 Handler 时首先需要创建 Handler 对象。
接下来,我们看下 Handler 的构造方法。
2、Handler构造方法源码分析
//frameworks/base/core/java/android/os/Handler.java
/* 构造方法一 */
public Handler() {
this(null, false);
}
/* 构造方法二 */
public Handler(Callback callback) {
this(callback, false);
}
/* 构造方法三 */
public Handler(Looper looper) {
this(looper, null, false);
}
/* 构造方法四 */
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/* 构造方法五 */
public Handler(boolean async) {
this(null, 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;
}
/* 构造方法七 */
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看到 Handler 有很多构造方法,常用的是构造方法一(实际也是调用的 :构造方法六)。
我们再来分析下构造方法六的源码:
//frameworks/base/core/java/android/os/Handler.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;
}
可以看到,这里调用了 Looper.myLooper()
方法,当 mLooper 为空时会抛出异常,提示我们需要先调用 Looper.prepare()
方法,我接下来看下 Looper 中的这两个方法。
3、Looper源码分析
Looper
是 Handler 的核心组成部分之一。它是一个消息循环器,负责管理消息队列,并按照先进先出的顺序处理消息。- 每个线程都可以创建自己的 Looper,并且主线程(UI 线程)默认就会创建一个 Looper。
- Looper 通过
Looper.prepare()
方法初始化,然后调用Looper.loop()
方法开启消息循环。
(1)、Looper.java
//frameworks/base/core/java/android/os/Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
从上面源码中可以看到 Looper 有 4 个成员变量:
- sThreadLocal:保存的是当前线程的 Looper。
- sMainLooper:Application 中主线程中的 Looper。
- mQueue:当前线程中的 MessageQueue。
- mThread:创建 Looper 的线程。
(2)、myLooper
//frameworks/base/core/java/android/os/Looper.java
/* Handler 构造方法六中调用的方法 */
public static Looper myLooper() {
// 返回当前线程中的 looper
return sThreadLocal.get();
}
从上面的源码可见 myLooper()
逻辑很简单,调用了 ThreadLocal 的 get() 方法。ThreadLocal 我们稍后再分析。
(3)、prepare
在 Handler 构造方法六中可以看到,如果 myLoop() 的结果为空会直接抛出异常,提示需要先调用 prepare()
方法,接下来分析下 prepare()
方法。
/* Handler 构造方法六中调用的方法 */
public static void prepare() {
prepare(true);
}
/* 带参数的 prepare 方法 */
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 构造方法 */
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepare()
方法中调用了 prepare(quitAllowed)
方法,这里判断了 Looper 是否为空。
如果当前线程已经创建了 Looper 直接抛出异常,也就是说一个线程中只能创建一个 Looper,经常使用 Handler 的小伙伴应该对这个异常很熟悉。
如果当前线程没有创建 Looper 会直接调用 Looper(quitAllowed)
的构造方法,创建一个 Looper 并创建一个 MessageQueue,然后保存一下当前线程的信息。
4、MessageQueue源码分析
MessageQueue
是 Looper 的另一个核心组成部分,它是一个消息队列,负责存储和管理 Message 对象。MessageQueue
提供了enqueueMessage()
和next()
等方法来实现消息的入队和出队操作。
我们先看下 MessageQueue 的具体实现:
//frameworks/base/core/java/android/os/Looper.java
final MessageQueue mQueue;
/* Looper 构造方法 */
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
再看下MessageQueue 的构造方法:
//frameworks/base/core/java/android/os/MessageQueue.java
private native static long nativeInit();
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue 的构造方法逻辑比较简单。
这里调用了一个 native 方法 nativeInit()
在 native 层进行了初始化。
感兴趣的朋友可以去查看 native 源码,文件如下:
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
至此, Handler 的创建流程已经分析完了,可以看到 Handler 创建流程如下图所示:
在创建 Handler 时:
第一步需要先调用 Looper.prepare(),该方法会初始化 Looper,创建 MessageQueue 和 ThreadLocal。
第二步会调用 Looper 中的 myLoop() 方法获取到 Looper 和 MessageQueue 保存到 Handler 中。
5、ThreadLocal 源码分析
如上,我们看见 第一步时,创建了 ThreadLocal 和 MessageQueue 。
(1)、ThreadLocal 到底是做什么的呢?
我们来分析下 ThreadLocal 的作用,源码如下:
ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
sThreadLocal.set(new Looper(quitAllowed)); // 设置变量信息
sThreadLocal.get(); // 读取变量信息
我们可以看到ThreadLocal在Android的消息循环机制中扮演了重要角色。
在Android中,每个线程都有自己的Looper对象,用于从MessageQueue中获取消息并执行。由于Looper对象是线程私有的,因此Android通过ThreadLocal来为每个线程维护自己的Looper实例。
具体来说:
-
ThreadLocal<Looper> sThreadLocal
定义了一个线程本地存储对象,用于存放当前线程的Looper实例。 -
sThreadLocal.set(new Looper(quitAllowed))
在当前线程中创建一个Looper对象,并通过set方法将其关联到当前线程。 -
sThreadLocal.get()
则可以在当前线程中获取之前设置的Looper对象。
通过这种方式,Android就实现了每个线程拥有自己的Looper实例,可以独立地从MessageQueue获取和处理消息,避免了线程间数据混乱和竞争的问题。
ThreadLocal的这种线程隔离机制,使得Android的消息循环模型可以在多线程环境下高效、安全地运行,同时也体现了ThreadLocal在实现线程数据隔离方面的优秀作用。
(2)、ThreadLocal 的具体实现
接下来分析下 ThreadLocal 的具体实现。
//java/lang/ThreadLocal.java
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
ThreadLocal 通过 threadLocalHashCode 来标识每一个 ThreadLocal 的唯一性。
threadLocalHashCode 通过 CAS 操作进行更新,每次 hash 操作的增量为 0x61c88647。
我们来看看 ThreadLocal 的 set() 方法。
//java/lang/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) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到通过 Thread.currentThread()
方法获取了当前的线程引用,并传给了 getMap(Thread)
方法获取一个 ThreadLocalMap 的实例。
在 getMap(Thread)
方法中直接返回 Thread 实例的成员变量 threadLocals。它的定义在 Thread 内部,访问级别为 package 级别:
//java/lang/Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
到了这里,可以看出,每个 Thread 里面都有一个 ThreadLocal.ThreadLocalMap
成员变量,也就是说每个线程通过 ThreadLocal.ThreadLocalMap
与 ThreadLocal
相绑定,这样可以确保每个线程访问到的 ThreadLocal 变量都是本线程的。
获取了 ThreadLocalMap 实例以后,如果它不为空则调用 ThreadLocalMap.ThreadLocalMap.set() 方法设值;
若为空则调用 ThreadLocal.createMap() 方法 new 一个 ThreadLocalMap 实例并赋给 Thread.threadLocals。
(3)、ThreadLocalMap的源码分析
下面我们分析一下 ThreadLocalMap 的实现,可以看到 ThreadLocalMap 有一个常量和三个成员变量:
//java/lang/ThreadLocal.ThreadLocalMap
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
其中 INITIAL_CAPACITY 代表这个 Map 的初始容量;table 是一个 Entry 类型的数组,用于存储数据;size 代表表中的存储数目; threshold 代表需要扩容时对应 size 的阈值。
Entry 类是 ThreadLocalMap 的静态内部类,用于存储数据。它的源码如下:
//java/lang/ThreadLocal.ThreadLocalMap
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry 类继承了 WeakReference<ThreadLocal<?>>,即每个 Entry 对象都有一个 ThreadLocal 的弱引用(作为 key),这是为了防止内存泄露。一旦线程结束,key 变为一个不可达的对象,这个 Entry 就可以被 GC 回收了。
ThreadLocalMap 类有两个构造函数,其中常用的是 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue):
//java/lang/ThreadLocal.ThreadLocalMap
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
构造函数的第一个参数就是本 ThreadLocal 实例(this),第二个参数就是要保存的线程本地变量。构造函数首先创建一个长度为 16 的 Entry 数组,然后计算出 firstKey 对应的哈希值,然后存储到 table 中,并设置 size 和 threshold。
通过上面分析可以看到 ThreadLocal 的工作原理如下:
如图所示,ThreadLocal 中有一个 ThreadLocalMap 其中以 ThreadLocal 作为 Key,以需要保存的值作为 Value。这样不同的线程访问同一个 ThreadLocal 时,获取到的值也就是各个线程存储时对应的值了。
分析了 ThreadLocal ,接下来,我们再来看看MessageQueue 。
6、MessageQueue 源码分析
(1)、Handler.sendMessage()
我们常用的发消息的方法如下:
//frameworks/base/core/java/android/os/Handler.java
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
上面不管哪种发消息的方式,最后都调用了 sendMessageDelayed()
方法。
//frameworks/base/core/java/android/os/Handler.java
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) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessageDelayed()
方法最后调用了 MessageQueue.enqueueMessage()
。
(2)、MessageQueue.enqueueMessage()
我们接着来看 enqueueMessage()
方法的实现:
//frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果消息队列里面没有消息,或者消息的执行时间比里面的消息早,
// 就把这条消息设置成第一条消息;
// 一般不会出现这种情况,因为系统一定会有很多消息。
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 如果消息队列里面有消息
needWake = mBlocked && p.target == null && msg.isAsynchronous();
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; // 把消息添加到最后
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
分析到这里可以看到,我们通过调用 Handler.sendMessage()
最后将 Message 添加到了 MessageQueue 的消息队列中。
在前面 Looper.loop()
方法中分析过,loop()
方法中有一个死循环一直在读取消息,当读取到刚才添加的消息后会回调到 Handler.dispatchMessage()
方法。
到这里, Handler 的工作流程大家应该已经很清楚了。
如下图所示,假设在 Thread 1 中创建了 Handler,那么 Thread 2 向 Thread 1 发送消息的过程。
Handler 机制就像是一个传送机器,Looper 就是传送轮一直在不停的旋转,MessageQueue 就是传送带跟着Looper 旋转来运输 Message,Handler 就是机械手在 Thread 2 中将 Message 放到传送带 MessageQueue 上,传送到 Thread 1 后再将 Message 拿下来通知 Thread 1 进行处理。
(3)、Handler.post() 源码
了解了 Handler 工作流程,我们继续来分析下另一种使用方式 Handler.post()
。
//frameworks/base/core/java/android/os/Handler.java
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
可以看到 post()
也是调用了 sendMessageDelayed()
方法。
(4)、getPostMessage 源码
我们再来看下 getPostMessage(r)
方法的实现。
//frameworks/base/core/java/android/os/Handler.java
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
原来这里创建了一个 Message,将 Runnable 放入了 Message 的 callback 上。
(5)、dispatchMessage 源码
那 Message 最后怎么处理的呢?
在 Looper.loop()
方法中有这么一句 msg.target.dispatchMessage(msg)
。
//frameworks/base/core/java/android/os/Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 处理 post 消息,稍后再分析
} else {
if (mCallback != null) {
// 回调到 Handler.handleMessage() 方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleCallback()
就是处理 Handler.post()
发送的消息:
//frameworks/base/core/java/android/os/Handler.java
private static void handleCallback(Message message) {
message.callback.run();
}
如此简单,就是拿到 Runnable 调用了 run()
方法。
至此, 关于Handler,Looper ,Message 这三者关系上面已经叙述的非常清楚了。
让我们首先总结一下:
-
Looper.prepare()方法会为当前线程创建一个Looper实例,其内部含有与之关联的MessageQueue对象。同一线程内只能调用一次prepare(),因此MessageQueue在线程内是单例的。
-
Looper.loop()会让当前线程进入无限循环模式,不断从MessageQueue中读取消息,并通过msg.target.dispatchMessage(msg)将消息分发给相应的Handler进行处理。
-
在构造Handler实例时,会获取当前线程的Looper,并将Handler的MessageQueue与Looper内部的MessageQueue建立关联。
-
调用Handler的sendMessage()方法时,会给Message设置target为当前Handler实例,并将Message加入关联的MessageQueue中等待分发。
-
在Handler的handleMessage()方法中,我们可以重写自定义的消息处理逻辑,这个方法最终会由Looper.loop()中的msg.target.dispatchMessage(msg)回调执行。
我们再来张图一目了然 :
五、结语
伴随着Android版本不断迭代,Handler机制也在持续优化和完善,Android 12中引入了高效模式MessageQueue和SyncQueueRemover工具等新特性。离开Handler,消息驱动架构也延伸出各种优秀的开源替代品,比如EventBus、RxJava。
未来Handler如何发展,我们拭目以待,不过这些基础底层原理的理解对开发者而言永远都是重中之重,期待您在实践中有更多思考和总结。