1 前言

​ Handler 即处理器,常用于跨线程通讯:线程A 和线程 B 拥有同一个 handler 对象,在线程 A 中使用 handler 的 sendMessage() 方法发送消息,在线程 B 中使用 handler 的 handleMessage() 方法处理消息。

​ Handler 家族主要有:Message、Handler、MessageQueue、Looper。

(1)应用场景

  • 子线程处理完耗时操作后,请求主线程更新 UI
  • 线程 A 定时给线程 B 发送消息
  • 线程 A 周期性给线程 B 发送消息

(2)Handler 家族

  • Message:消息类,通过 obtain() 方法可以生成一个 message 对象,每个 message 对象必须有一个 handler 对象;
  • Handler:消息处理器,用于生成(obtainMessage)和处理消息(handleMessage);
  • MessageQueue:消息队列,用于存储消息,通过 enqueueMessage() 放入消息,next() 取出消息;
  • Looper:循环器,用于取出消息,并通知 handler 执行消息。

(3)Handler 机制基本原理

​ 假设在线程 A 中创建了 handler 对象,重写了 handler 的 handleMessage() 方法,并且将 handler 对象传给了线程 B,则线程 B 和线程 A 间的通讯如下:

  1. 生成消息:线程 B 通过 handler.obtainMessage() 生成消息(内部会调用 Message.abtain() 方法,并且会把当前 handler 传给 message 对象);
  2. 发送消息:线程 B 通过 handler.sendMessage() 发送消息,再调用 mQueue.enqueueMessage() 将消息放入消息队列;
  3. 取出消息:线程A 中 mLooper 对象的 loop() 方法一直循环执行(若 mQueue 中没数据会等待),其内部会调用 mQueue 对象的 next() 方法,取出一个消息 msg;
  4. 执行任务:线程 A 中取出的 msg 对象在创建时已绑定 handler,通过调用 msg.target.dispatchMessage() 方法,通知 handler 执行 handleMessage() 方法。

2 Handler 机制源码解析

2.1 Message

(1)类图

(2)生成消息

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
} public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}

​ sPool 是可用的空消息对象池 ,其本质是链表,sPool 指向链表的头结点,next 指向下一个节点。

(3)处理消息

msg.target.dispatchMessage(msg)

//最终会执行如下一种
msg.callback.run()
handler.mCallback.handleMessage(msg)
handler.handleMessage(msg)

​ target 是 Message 绑定的 handler 对象。

2.2 Handler

(1)类图

(2)Looper 对象的获取

​ 若 handler 在主线程中创建,则不需要显式地给 handler 传递 Looper 对象。在主线程被创建时,系统会为主线程创建 Looper 对象,并开启消息循环。

public Handler() {
this(null, false);
} public Handler(Handler.Callback callback, boolean async) {
...
mLooper = Looper.myLooper(); //获取与当前线程绑定的Looper对象
...
mQueue = mLooper.mQueue;
...
}

​ 若 handler 在子线程中创建,则需要显示地传递 looper 对象,因为子线程在创建时不会创建 Looper 对象。

public Handler(Looper looper) {
this(looper, null, false);
} public Handler(Looper looper, Handler.Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
...
}

(3)生成消息

public final Message obtainMessage() {
return Message.obtain(this);
}

​ 将 this 赋值给 message.target,与 message 对象绑定。

(4)发送消息

public final boolean sendMessage(Message msg) {
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;
...
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);
}

​ 从 enqueueMessage() 方法中可以看到,在消息入队之前,将 this 赋值给了 msg,确保每个 msg 都能绑定一个 handler

(5)发送任务

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;
}

​ 可以看到,即使发送的是 runnable,也会封装为 message。

(6)处理消息

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
} private static void handleCallback(Message message) {
message.callback.run();
} public void handleMessage(Message msg) {
} public interface Callback {
public boolean handleMessage(Message msg);
}

​ 从 dispatchMessage() 方法中可以看到,处理消息的次序为:

  1. msg.callback.run()
  2. mCallback.handleMessage(msg)
  3. handleMessage(msg)

2.3 MessageQueue

(1)类图

(2)存储消息

boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
...
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; //插入消息
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr); //唤醒 looper
}
}
return true;
}

​ mMessages 是消息队列的链头。

(3)取出消息

Message next() {
...
int nextPollTimeoutMillis = 0;
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis); //等待
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else { //取出消息
...
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
...
return msg;
}
}
...
}
...
}
}

2.4 Looper

(1)类图

(2)创建 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)); //创建Looper对象,并与当前线程绑定
}

(3)创建 MessageQueue

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

(4)循环

public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); //可能会等待
...
msg.target.dispatchMessage(msg); //处理消息
...
msg.recycleUnchecked(); //回收消息
}
}

3 应用

3.1 应用一

(1)主线程(处理消息)

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println("打印消息");
}
};

(2)子线程(发送消息)

handler.sendEmptyMessage(0x111);

3.2 应用二

(1)主线程(处理消息)

Handler handler = new Handler();

(2)子线程(发送消息)

handler.post(new Runnable() {
@Override
public void run() {
System.out.println("打印消息");
}
});

4 拓展

​ 第3节中的案例都是子线程给主线程发送消息,但是某些场景需要主线程给子线程发送消息,或2个子线程间发送消息。默认情况下,系统会为主线程创建 Looper 对象,并开启消息循环,但新建的子线程中不会创建 Looper 对象,此时,就需要应用到 HandlerThread 类。

(1)HandlerThread 类图

(2)HandlerThread 核心源码

// 消息循环,通过threadHandler.start()开启循环
public void run() {
mTid = Process.myTid();
Looper.prepare(); //创建looper对象,并与当前线程绑定
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); //消息循环
mTid = -1;
} //获取handler,并注入mLooper
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}

(3)应用

​ 线程 A(处理消息)

HandlerThread handlerThread = new HandlerThread("A");
handlerThread.start();
//Handler handler = handlerThread.getThreadHandler();
Handler handler = new Handler(handlerThread.getLooper());

​ 线程 B(发送消息)

handler.post(new Runnable() {
@Override
public void run() {
System.out.println("打印消息");
}
});

​ 声明:本文转自【Android】Message、Handler、MessageQueue、Looper 详解

【Android】Message、Handler、MessageQueue、Looper 详解的更多相关文章

  1. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  2. 异步消息处理(Message, Handler, MessageQueue, Looper)

    AsyncTask 适用于单线程任务处理,多任务处理还是 Message/Handler 处理方便一些 主要使用方式: 1,创建子类继承自 Handler 类,覆盖 handleMessage(Mes ...

  3. Android App优化之ANR详解

    引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App ...

  4. [转]Handler MessageQueue Looper消息循环原理分析

    Handler MessageQueue Looper消息循环原理分析   Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...

  5. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解 (旧版本 | 仅作参考)

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

  6. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

  7. Android Telephony分析(五) ---- TelephonyRegistry详解

    本文紧接着上一篇文章<Android Telephony分析(四) —- TelephonyManager详解 >的1.4小节.从TelephonyRegistry的大部分方法中: 可以看 ...

  8. Android Telephony分析(三) ---- RILJ详解

    前言 本文主要讲解RILJ工作原理,以便更好地分析代码,分析业务的流程.这里说的RILJ指的是RIL.java (frameworks\opt\telephony\src\java\com\andro ...

  9. Android Telephony分析(二) ---- RegistrantList详解

    前言 本文主要讲解RegistrantList的原理,以及如何快速分析RegistrantList相关的代码流程.在Telephony模块中,在RIL.Tracker(ServiceStateTrac ...

  10. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

随机推荐

  1. 安卓系统如何使用谷歌框架下的app?

    1.问题 安卓系统从理论上无法使用谷歌框架下的应用(比如像GMail,YouTube,Google play等等),会导致一些麻烦(闪退,卡在登陆界面等等) 注意:使用前提是会魔法,否则请绕道 2.解 ...

  2. 【MicroPython】用 c 添加接口 -- 给 module 添加 function

    [来源]https://www.eemaker.com/micropython-c-modfunc.html

  3. Oracle 专用模式与共享模式的学习与思考

    Oracle 专用模式与共享模式的学习与思考 说明 Oracle数据库中的专用模式和共享模式是两种不同的数据库运行模式,它们在应用场景和权限管理上有所不同. 专用模式(Dedicated Mode): ...

  4. [转帖]TIDB - TIDB集群的扩容和缩容及TIUP指令说明

    一.TIUP工具简介 前面介绍了使用TIUP搭建TIDB集群,本篇文章详细介绍下使用TIUP对集群进行扩容和缩容. 在面对双十一这种流量突峰的场景,我们平常的TIDB集群有可能承受不住,因此需要提前进 ...

  5. [转帖]Mysql 常用命令行,持续补充

    https://www.cnblogs.com/wzj1223/p/13152446.html 1.常用命令行 # 登录Mysql mysql -uroot -proot # 查看所有数据库 show ...

  6. [转帖]shell脚本之awk命令——按列求平均值、最大值、最小值

    文章目录 写在前面 awk求平均值 awk求最大值 awk求最小值 awk求极值.均值的实际应用 写在前面 awk命令求极值和均值需要熟悉该命令的基本用法,如果你不熟悉该命令,请先阅读shell脚本之 ...

  7. [转帖]ChatGPT发展历程、原理、技术架构详解和产业未来 (收录于先进AI技术深度解读)

    https://zhuanlan.zhihu.com/p/590655677 陈巍谈芯::本文将介绍ChatGPT的特点.功能.技术架构.局限.产业应用.投资机会和未来.作者本人曾担任华为系自然语言处 ...

  8. [转帖]Linux实用技巧——find查找指定时间内修改过的文件或目录

    https://cloud.tencent.com/developer/article/1694949 解决方案 例:查找出五分钟内修改过的文件 [root@mobius ~]$ find ./* - ...

  9. 商智C店H5性能优化实战

    前言 商智C店,是依托移动低码能力搭建的一个应用,产品面向B端商家.随着应用体量持续增大,考虑产品定位及用户体验,我们针对性能较差页面做了一次优化,并取得了不错的效果,用户体验值(UEI)从一般提升到 ...

  10. git提交出现running pre-commit hook: lint-staged

    现象 今天提交代码的时候出现了 > running pre-commit hook: lint-staged Stashing changes... [started] Stashing cha ...