【Android】从源码角度理解Handler机制
【Android】从源码角度理解Handler机制
文章目录
- 【Android】从源码角度理解Handler机制
- 1. Handler简介
- 一个线程可以有多少个Looper?有多少个MessageQueue?
- 为什么主线程不会因为Looper阻塞?
- 2. Handler的基本用法
- 3. Handler机制解析
- 3.1 Looper的创建
- 3.2 Handler与线程的关联
- 3.3 Looper.loop()
- 3.4 线程的切换
- 3.5 小结
- 4. Handler的基本使用
- 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的基本用法
Handler handler = new Handler(){@Overridepublic void handleMessage(final Message msg) {//这里接受并处理消息}
};
//发送消息
handler.sendMessage(message);// 传递数据
handler.post(runnable);// 执行代码
实例化一个Handler重写handleMessage()
方法,然后在需要的地方调用send
以及post
系列方法就行了,非常简单易用,同时支持延时消息。
3. Handler机制解析
一个普通线程如果使用Handler,那么完整的示例应该是这样的:
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对象,看一下源码:
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的构造函数:
private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}
构造函数中创建了一个MessageQueue,同时Looper持有当前线程。
这就说明一个线程只能有一个Looper和一个MessageQueue。
需要注意:Android主线程,即UI线程执行的是另一个函数prepareMainLooper()
:
@Deprecatedpublic 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分析:
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:
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源码:
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
方法,在这里处理单条消息:
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {Message msg = me.mQueue.next(); // might blockif (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
源码:
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。
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的方法调用逻辑:
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的基本使用
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;@Overrideprotected 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() {@Overridepublic void onClick(View view) {postMessage();}});// 定义 Handler 并重写 handleMessagemHandler = new Handler(Looper.getMainLooper()) {@Overridepublic 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 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"><Buttonandroid: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"/><TextViewandroid: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 的线程;