【Android】Handler用法及原理解析
文章目录
- 用处
- 基本用法
- 用法一:使用sendMessage和handleMessage方法
- 用法二:使用post方法
- 法一工作原理
- Handler的sendMessage
- Message
- 成员变量
- MessageQueue
- Looper
- 主线程自动初始化
- 子线程手动创建
- prepare
- loop
- Handler的dispatchMessage
- 法二工作原理
- Handler的post
- Handler的dispatchMessage
- 总结
Handler
是用于在不同线程之间进行消息传递的机制。它与
Looper
和
MessageQueue
一起工作,帮助在不同线程间传递和处理消息,特别是在主线程与子线程之间进行通信。
用处
- 子线程通过
Handler
更新 UI - 用于线程间通信
基本用法
用法一:使用sendMessage和handleMessage方法
-
创建Handler:通常在主线程中创建一个
Handler
,用于处理从其他线程发送的消息。Handler handler = new Handler();
-
发送消息:在子线程中,通常使用
handler.sendMessage()
来向主线程发送消息。new Thread(new Runnable() {@Overridepublic void run() {Message message = Message.obtain();message.what = 1;handler.sendMessage(message);} }).start();
-
处理消息:主线程中的
Handler
会调用handleMessage()
方法来处理接收到的消息。Handler handler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:// 处理消息break;}} };
用法二:使用post方法
-
创建
Handler
:Handler handler = new Handler(Looper.getMainLooper());
-
使用
post
方法:可以直接在Handler
中执行需要在主线程上运行的代码块。handler.post(new Runnable() {@Overridepublic void run() {// 执行主线程任务} });
区别:
sendMessage
: 适用于更复杂的消息处理和任务调度,特别是需要在多个线程之间通信时。它依赖于Handler
类,通过消息机制来传递和处理任务。post
: 更加简洁和直接,主要用于在主线程中执行任务,尤其是需要更新 UI 的情况。适合用于在视图中直接执行代码而不涉及复杂的线程通信。
法一工作原理
主要依靠以下
-
Looper:负责不断从
MessageQueue
中取出消息并分发给相应的Handler
进行处理。 -
Handler:
Handler
是一个用于发送和处理消息的类。它可以用来将Message
或Runnable
对象发送到消息队列中,并在对应的线程中处理这些消息。 -
Message:用于在
Handler
和Looper
之间传递消息。 -
MessageQueue:
MessageQueue
是消息的容器,负责存储和管理待处理的消息。维护了一个消息队列,按顺序处理消息。
举个例子:
天气预报:子线程获取到了天气数据,然后使用
Message
将数据包装,通过Handler
对象的sendMessage
方法将Message
对象插入到消息队列(MessageQueue
)中,主线程通过Looper.loop
方法死循环检查MessageQueue
中是否有消息,主线程的Handler
会通过handleMessage()
方法接收这个Message
Handler的sendMessage
handler.sendMessage(message);
sendMessageDelayed
,sendEmptyMessageAtTime
,sendEmptyMessageDelayed
,sendEmptyMessage
这些Handler
类中发送消息的方法最终调用的方法都是sendMessageAtTime
。
sendMessageAtTime
方法中再调用enqueueMessage
插入消息到消息队列
Message
Message
类用于Handler
和Looper
之间的消息传递
Message
类定义一个包含描述和任意数据对象的消息,该消息可以发送给Handler
。此对象包含两个额外的 int 字段和一个额外的对象字段,允许您在许多情况下不进行分配。
虽然Message
的构造函数是公共的,但获取其中一个的最佳方法是调用Message.obtain()
或Handler.obtainMessage()
方法之一,它们会从回收对象池中拉出它们。
成员变量
部分成员变量分析
// what 字段是一个用户自定义的消息代码,用来标识消息的类型。
public int what;//arg12 是一个整型值字段,用来传递简单的整型数据。
public int arg1;
public int arg2;// obj 是一个任意的对象,允许发送者将任何类型的对象传递给接收者。
public Object obj;// 可选的 Bundle,用于传递复杂数据。
Bundle data;// target 是消息最终要传递到的 Handler,负责处理该消息。
Handler target;// 用于将 Message 对象串联在一起的指针,
Message next;
获取消息的首选方法是调用Message. obtain()
最终实际都是通过obtain()
方法获取的Message
对象
obtain()
源码:
// 从全局池返回一个新的 Message 实例。这允许我们在很多情况下避免分配新对象。
public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();
}
用于从全局池(sPool
)中返回一个新的 Message
实例。如果池中有可用的消息对象,它将从池中获取,否则创建一个新的。可以优化性能
MessageQueue
MessageQueue
是一个用于保存消息(Message
)的队列,其内部实现为一个单链表结构。
-
enqueueMessage(Message msg, long when)
:用于将消息插入到队列的链表中,when
参数决定消息应该在什么时候被执行。消息按when
的顺序排列,越早需要执行的消息排在前面。 -
next()
:此方法用于从队列中取出下一个需要处理的消息。如果当前队列为空,线程会进入阻塞状态,直到新的消息到来。
Looper
用于运行线程消息循环的类。
Looper
是一种管理消息队列并将消息分发到线程的机制。每个线程都有自己的消息队列和消息循环,这就是由Looper
来实现的。默认情况下,线程没有与之关联的消息循环;若要创建一个消息循环,请在要运行循环的线程中调用prepare ,然后loop以让其处理消息,直到循环停止。
主线程自动初始化
- 主线程(UI线程)是应用启动时的默认线程,负责处理所有与用户界面相关的任务。Android 系统在应用启动时会自动为主线程创建一个
Looper
,因此你无需手动调用Looper.prepare()
和Looper.loop()
。 - 这个主线程的
Looper
用来处理 UI 事件和系统消息,如用户点击、触摸事件等,这确保了 UI 的更新必须在主线程上执行
主线程ActivityThread
的main
方法中自动初始化了:
public static void main(String[] args) {...// 为主线程准备消息循环,创建与当前线程关联的Looper,并设置为主线程的LooperLooper.prepareMainLooper();...// 初始化主线程的 Handler,用于处理消息队列中的消息if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}...// 启动消息循环,开始处理消息队列中的消息,这一步将一直运行,直到应用程序终止Looper.loop();// 如果消息循环意外退出,抛出异常。正常情况下,主线程的消息循环不应该终止throw new RuntimeException("Main thread loop unexpectedly exited");
}
子线程手动创建
-
当你在 子线程 中需要处理消息时(例如子线程需要执行长时间运行的任务,并且希望在任务完成后更新 UI),你需要手动为这个子线程创建
Looper
。步骤通常包括:- 调用
Looper.prepare()
:为当前线程创建一个Looper
实例。 - 创建
Handler
- 调用
Looper.loop()
:进入消息循环,不断从消息队列中获取消息并分发给相应的Handler
处理。
示例:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler(Looper.myLooper()) {public void handleMessage(Message msg) {// process incoming messages here}};Looper.loop();} }
- 调用
prepare
作用:
在当前线程中创建一个
Looper
,以便线程可以拥有一个消息循环(MessageQueue
)。通常在子线程中需要手动调用,主线程已经自动调用了Looper.prepare()
由于Looper构造方法被私有了,只能通过调用
prepare()
方法
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
这里的sThreadLocal是一个map类型容器,可以保证了1个线程中只有1个对应的Looper
1个线程中可以有多个Handler,1个线程中只有1个对应的Looper
如果已经存在Looper就抛出异常,否则new一个Looper
放入sThreadLocal
中
loop
- Looper负责管理线程的消息循环,它在
loop()
方法中不断从MessageQueue中取出消息,并通过Handler的dispatchMessage()
方法将消息分发给对应的Handler进行处理。
public static void loop() {// 获取当前线程的looper对象final Looper me = myLooper();// 防止没有在该方法调用前调用prepare方法if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}...// 使用死循环来遍历消息队列,挑出需要执行的 Message 并分发for (;;) {if (!loopOnce(me, ident, thresholdOverride)) {return;}}}
- 在Looper的循环过程中,取出的每个Message对象都会通过
Handler
的dispatchMessage()
方法传递给目标Handler,最终由目标Handler的handleMessage()
方法处理消息。
private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {// 从消息队列中获取下一个消息。如果队列中没有消息,next() 可能会阻塞。// 当消息队列为空(即没有消息时),返回 null,表示消息队列正在退出。Message msg = me.mQueue.next(); if (msg == null) {// 如果消息为空,表示消息队列正在退出,终止循环。return false;}...try {// msg.target是指向一个Handler对象的引用,即消息的接收者// 分发消息到Handler进行处理,实际处理逻辑在msg.target.dispatchMessage()中。msg.target.dispatchMessage(msg);...} catch (Exception exception) {...} finally {...}...// 回收 Message 对象,避免对象的重复创建,提升性能。msg.recycleUnchecked();// 返回 true,表示循环可以继续处理下一个消息。return true;
}
Handler的dispatchMessage
没有使用post方法的话,调用重写的handleMessage
方法
// 处理系统消息的方法。
public void dispatchMessage(@NonNull Message msg) {// 检查消息是否包含回调函数if (msg.callback != null) {// 如果有回调函数,处理回调handleCallback(msg);} else {// 如果没有回调函数// 检查是否设置了自定义的回调处理器if (mCallback != null) {// 如果自定义回调处理器处理了消息,直接返回if (mCallback.handleMessage(msg)) {return;}}// 如果自定义回调处理器没有处理消息,则调用默认的消息处理方法handleMessage(msg);}
}
法二工作原理
Handler的post
handler.post(new Runnable() {@Overridepublic void run() {}
});
post方法,一般以匿名内部类的形式发送Runnable
对象,在Runnable
对象重写的run()
方法中直接对UI进行更新
post类方法用以发送Runnable对象,send类方法用以发送Message对象,二者并不矛盾也不重复
观察Message
类,可以发现安卓把Runnable对象作为Message的成员变量
sendMessageDelayed(getPostMessage(r), 0)
post
方法又调用了sendMessageDelayed
,注意getPostMessage(r)
,在这个方法里就把我们的Runnable对象赋值到了Runnable
类型的callback
上了
private static Message getPostMessage(Runnable r) {// 获取一个 Message 实例Message m = Message.obtain();// 将传入的 Runnable 对象设置为 Message 的回调函数。m.callback = r;// 返回配置好的 Message 对象。return m;
}
Handler的dispatchMessage
public void dispatchMessage(@NonNull Message msg) {if (msg.callback != null) {// 如果消息包含 callback,调用 handleCallback 方法handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return; // 自定义回调处理器处理了消息}}handleMessage(msg); // 默认处理消息}
}
发现该消息有Runnable
就调用handleCallback
方法执行 Runnable
对象。
private static void handleCallback(Message message) {message.callback.run();}
其他部分与法一原理一致
总结
感谢您的阅读
如有错误烦请指正
参考:
- Handler类中发送消息-post()和postDelay()方法精炼详解
- android Handler机制原理解析(一篇就够,包你形象而深刻)_android handler的机制和原理-CSDN博客
- Handler 都没搞懂,拿什么去跳槽啊?0. 前言 做 Android 开发肯定离不开跟 Handler 打交道,它通 - 掘金 (juejin.cn)
- Android 消息机制(Handler + MessageQueue + Looper) - 知乎 (zhihu.com)
- Android——Handler一篇就看懂_android handler-CSDN博客