【Android】从源码角度理解Handler机制
文章目录
- 【Android】从源码角度理解Handler机制
-
- [1. Handler简介](#1. Handler简介)
- [2. Handler的基本用法](#2. Handler的基本用法)
- [3. Handler机制解析](#3. Handler机制解析)
-
- [3.1 Looper的创建](#3.1 Looper的创建)
- [3.2 Handler与线程的关联](#3.2 Handler与线程的关联)
- [3.3 Looper.loop()](#3.3 Looper.loop())
- [3.4 线程的切换](#3.4 线程的切换)
- [3.5 小结](#3.5 小结)
- [4. Handler的基本使用](#4. Handler的基本使用)
- [5. 总结](#5. 总结)
1. Handler简介
Handler通常用来做主线程与子线程之间的通信工具。比如,在子线程中进行耗时操作(网络请求、数据库操作等),子线程在获取数据后,使用Handler来进行UI更新:子线程拿到数据后,不直接更新界面,而是把数据通过一个消息(Message)发送出去;主线程这边,会提前准备好一个"接收器"------Handler对象。这个Handler对象会专门接受子线程发送来的数据,把获取到的数据显示在界面上。
接下来介绍Handler机制的四个组成部分:
-
Handler:消息的发送者和处理者
-
MessageQueue:消息队列,存储待处理的消息
-
Looper:消息循环器,不断从队列中取出消息并分发,同时负责关联线程
-
Message:消息载体,包含数据和目标 Handler
你可以把以上四个部分想象成一条生产线的不同部分:
-
Message就是待加工的产品,线程之间要传递的消息就记录在这上面;
-
Handler 就像一个生产线上的工人,线程要发送消息,就通过Handler的
sendMessage()
方法发送出去;收到产品后,也是由Handler(目标Handler)handleMessage
方法读取并处理该产品。 -
MessageQueue 就像一条流水线上的传送带,所有发出去的"产品"都会先放在这里,排着队等待被加工处理。每个线程有且只有一条自己的"传送带"。
-
Looper 这个角色就有点像线长,负责管理每条流水线,它会不断检查(通过执行
loop
方法进入一个无限循环)传送带上有没有新"产品"。一旦发现有,它就会把产品取出来,然后递给对应的Handler(工人)去处理。
简单来说,就是线程A加工了一件"产品"(Message),交给自己的"工作人员"(Handler)发出去,这件产品会先放在"传送带"(MessageQueue)上。然后"线长"(Looper)会不停地检查传送带,一看到有产品,就把它取出来,交给对应的"工人"(Handler)去加工处理。这样,线程之间就能顺利地传递信息了。

一个线程可以有多少个Looper?有多少个MessageQueue?
一个独立的Thread最多只能拥有一个Looper实例,并且每个Looper都与其唯一的MessageQueue紧密关联。因此,如果一个Thread被配置为运行消息循环(即它拥有一个Looper),那么它将精确地拥有一个Looper和一个MessageQueue。这种一对一的映射是Android健壮的异步处理模型的基础,确保了线程间通信的可预测性和安全性,尤其对于至关重要的主(UI)线程而言。
为什么主线程不会因为Looper阻塞?
- Android 主线程的
Looper.loop()
在消息空闲时会通过epoll
进入休眠状态- 当有新消息(如触摸事件、Handler消息)时会被 native 层唤醒
2. Handler的基本用法
java
Handler handler = new Handler(){
@Override
public void handleMessage(final Message msg) {
//这里接受并处理消息
}
};
//发送消息
handler.sendMessage(message);// 传递数据
handler.post(runnable);// 执行代码
实例化一个Handler重写handleMessage()
方法,然后在需要的地方调用send
以及post
系列方法就行了,非常简单易用,同时支持延时消息。
3. Handler机制解析
一个普通线程如果使用Handler,那么完整的示例应该是这样的:
java
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
我们可以通过这个示例,一步步理解Handler到底是怎么实现的。
3.1 Looper的创建
首先在run方法中通过Looper.prepare();
创建一个Looper对象,看一下源码:
java
public static void prepare() {
prepare(true);
}
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));
}
在有参方法中,借助ThreadLocal
类来检查当前线程中是否已经存在Looper,存在则抛出异常,因为一个线程只能有一个Looper。如果不存在,创建一个新的Looper并存入当前线程的ThreadLocal
。简单来说就是Handler通过ThreadLocal
类,实现Looper和线程的绑定功能 。通常来说UI线程(主线程)会自动创建Looper,其他线程则需要手动调用。
继续看Looper的构造函数:
java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
构造函数中创建了一个MessageQueue,同时Looper持有当前线程。
这就说明一个线程只能有一个Looper和一个MessageQueue。
需要注意:Android主线程,即UI线程执行的是另一个函数prepareMainLooper()
:
java
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
可以看到有两处区别:
- 增加了一个sMainLooper变量,方便在其他线程获取主线程的Looper,即
getMainLooper()
- 执行
prepare()
时参数是false,也就是不允许退出,所以主线程的Looper没法手动退出。
3.2 Handler与线程的关联
从创建Handler分析:
java
public Handler(@Nullable Callback callback, boolean async) {
// ...
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;
// ...
}
在创建Handler时,首先通过Looper的myLooper()
方法获取线程的Looper:
java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myLooper
的代码很简单,是通过ThreadLocal类来获取线程的Looper,这里就体现了ThreadLocal
的作用:自动根据当前线程来获取对应的Looper,这样避免了传参,更加安全(防止传错)。
回到构造方法中:得到线程的Looper后,检查Looper是否存在,不存在就抛出异常,也就是说在创建Handler之前一定要先创建Looper。Looper存在则会把Looper持有的MessageQueue共享到Handler中。
到此,Handler就拿到了Looper和MessageQueue对象,就与Looper关联上了。也就是说,Handler与线程的关联是靠Looper实现的。
3.3 Looper.loop()
最后就是启动Looper,执行Looper.loop(),loop源码:
java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride = getThresholdOverride();
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
loop中最核心的就是for循环,实现无限循环处理队列中单条消息。
可以看到每次循环会执行loopOnce
方法,在这里处理单条消息:
java
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
// 注意,这里是线程结束。如果仅仅是MessageQueue为空,并不执行到这里,那种情况在queue.next()中,后面会讲到。
return false;
}
try {
// 处理消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
} catch (Exception exception) {
// ...
} finally {
// ...
}
// ...
}
loopOnce
中,循环每一次都通过me.mQueue.next()
;获取消息,然后调用msg.target.dispatchMessage(msg);
处理消息。这里target就是一个Handler对象,所以就调用Handler中的dispatchMessage
方法分发Message给对应对象处理。

Handler中dispatchMessage
源码:
java
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到如果msg有callback,先执行callback,这个callback就是一个Runnable。
当我们使用handler的post(Runnable)
等函数,handler会自动将runnable对象包装成Message,并将runnable对象赋值给这个msg的callback。
java
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;
}
如果没有callback则继续执行,最后会给handleMessage(msg)
函数处理,这就是我们非常熟悉的了,我们创建handler会重写这个方法处理msg。
总结: Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg)
回到了 Handler 来分发消息,以此来完成消息的回调。
3.4 线程的切换
梳理一下3.3的方法调用逻辑:
java
Thread.foo(){
Looper.loop()
-> MessageQueue.next()
-> Message.target.dispatchMessage()
-> Handler.handleMessage()
}
显而易见,Handler.handleMessage() 所在的线程最终由调用 Looper.loop() 的线程所决定。
平时我们用的时候从异步线程发送消息到 Handler,这个 Handler 的 handleMessage()
方法是在主线程调用的,所以消息就从异步线程切换到了主线程。
图解:

3.5 小结
Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。
- Looper :负责关联线程以及消息的分发在该线程下**从 MessageQueue 获取 Message,分发给 Handler **;
- MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
- Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。
Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。
线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。
4. Handler的基本使用
java
package com.example.handlertest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
private Handler mHandler;
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
textView = findViewById(R.id.textView);
button = findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
postMessage();
}
});
// 定义 Handler 并重写 handleMessage
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
String data = (String) msg.obj;
textView.setText(data);
break;
case 2:
Toast.makeText(MainActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
break;
}
}
};
}
private void postMessage() {
Toast.makeText(this, "加载中,请稍后...", Toast.LENGTH_SHORT).show();
// 在子线程发送消息
new Thread(() -> {
try {
// 模拟网络请求
Thread.sleep(2500);
boolean isSuccess = true;
// 通过 Message 传递数据
Message msg = mHandler.obtainMessage();
if (isSuccess) {
msg.what = 1;
msg.obj = "请求成功,数据已加载";
} else {
msg.what = 2;
}
mHandler.sendMessage(msg); // 发送消息
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
在主线程中定义 Handler 并重写 handleMessage,处理成功则简单设置textView的文本为msg,失败就弹一个提示失败的Toast
另外,定义一个按钮模拟耗时操作,比如模拟2.5秒的网络请求,在子线程中通过mHandler.obtainMessage();
使用主线程的Handler从消息池中复用Message(也可以每次new Message,不推荐),设置msg后发送消息到主线程的MessageQueue中处理,默认是处理成功,效果如下:

xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="执行请求"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/textView"
android:layout_marginBottom="20dp"
/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
5. 总结
1.Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,MessageQueue 负责消息管理;
2.在创建 Handler 之前一定需要先创建 Looper;
3.Looper 有退出的功能,但是主线程的 Looper 不允许退出;
4.Runnable 被封装进了 Message,可以说是一个特殊的 Message;
5.Handler.handleMessage()
所在的线程是 Looper.loop() 方法被调用的线程,也可以说成 Looper 所在的线程,并不是创建 Handler 的线程;