Android Handler 源码解析
在 Android 中,Handler 是一种强大的机制,用于在不同的线程之间进行通信。通过 Handler,你可以轻松地将任务从一个线程发送到另一个线程,通常用于在后台线程执行任务后更新UI。同时handler机制也是Android主线程运行的原理,了解了主线程的运行原理也就可以知道leakCancry的实现原理。
1. Handler 的基本原理
在 Handler 的背后,有三个核心概念:消息队列(Message Queue)、消息(Message)、Looper。让我们逐一了解它们的作用。
1.1 消息队列(Message Queue)
消息队列是一个先进先出(FIFO)的数据结构(在MessageQuene中使用链表来实现的),用于存储待处理的消息。每个 Handler 关联一个消息队列,这个队列负责存储和处理发送到该 Handler 的消息。
1.2 消息(Message)
消息是 Handler 处理的基本单元。每个消息都包含一个标识符(what)、处理消息的目标 Handler(target)、可选的数据(obj)以及处理消息的方法(callback)等信息。消息对象通过消息队列在不同线程间传递。
1.3 Looper
Looper 是一个线程局部的类,用于管理消息队列。一个线程只能有一个 Looper,而主线程默认已经有一个 Looper。在子线程中使用 Handler 之前,你通常需要调用 Looper.prepare()
和 Looper.loop()
来初始化和启动消息循环。
2. Handler 的创建与使用
2.1 创建 Handler
在主线程中创建 Handler 时,它会自动与主线程的 Looper 关联。在其他线程中使用 Handler 时,你需要显式地将 Handler 与该线程的 Looper 关联。
// 在主线程中创建 Handler
Handler handler = new Handler();
// 在子线程中创建 Handler
Handler handler = new Handler(Looper.getMainLooper());
2.2 发送消息
使用 Handler.sendMessage(Message msg)
方法可以向消息队列发送消息。
Message message = handler.obtainMessage();
message.what = MESSAGE_ID;
message.obj = someObject;
handler.sendMessage(message);
2.3 处理消息
通过重写 Handler.handleMessage(Message msg)
方法,可以处理收到的消息。
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
3. Handler 的核心源码解析
一下源码基于Android最新版本的SDK34 ,不同版本的sdk代码结构有所不同,但是基本代码逻辑是不变的
3.1 首先找到ActivityThread
ActivityThread
是 Android 系统中一个关键的类,它负责应用程序的主线程(UI 线程)的管理,以及处理与应用程序组件(如 Activity、Service、BroadcastReceiver 等)的生命周期相关的任务。在 Android 中,每个应用程序都会有一个 ActivityThread
实例,它是整个应用程序的入口点,java中的main方法就是在里面调用的。
代码结构如下
public class ActivityThread {
public static void main(String[] args) {
// 1.在主线程中创建消息循环(Looper)
Looper.prepareMainLooper();
// 创建ActivityThread对象
ActivityThread thread = new ActivityThread();
// 初始化主线程的一些参数和状态
thread.attach(false);
// 2.启动消息循环,处理消息队列中的消息
Looper.loop();
// 程序执行到这里时,不会继续执行,因为 Looper.loop() 是一个无限循环
}
}
3.1.1 Looper.prepareMainLooper();//1.创建Looper
通过ThreadLocal 创建一个线程唯一(线程内单例)的Looper 对象,他是线程安全的,和synchronized区别是
synchronized
机制采用了以时间换空间的方式- 而
ThreadLocal
采用了以空间换时间的方式
同时也可以看到prepare 不能调用两次,否则会抛出异常
同时在Looper的构造函数里面新建了一个MessageQueue
那么也就是说:一个线程中Looper对象唯一,也唯一对应一个MessageQueue对象
这里 可以可以得出结论 1个Thread----> 1个Looper------> 1 个MessageQueue----->多个Message---->多个Handler
3.1.2 Looper.loop(); 消息循环
public static void loop() {
//获取到上一步创建的Looper对象
final Looper me = myLooper();
for (;;) {//死循环
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // 1.might block
if (msg == null) {//调用quit会导致msg为null
// No message indicates that the message queue is quitting.
return false;
}
//。。省略
msg.target.dispatchMessage(msg);//消息分发处理
msg.recycleUnchecked();//消息回收
return true;//返回true继续死循环
}
why 死循环?因为我们程序是需要一直运行的,那一直循环,不会很浪费系统资源吗?熟悉Looper的Android开发都知道,Android的主线程是有阻塞和唤醒机制的,在没有消息处理的时候Android主线程会进入阻塞,让出cpu时间,不会浪费系统资源,等到下一条消息到来的时候(enqueueMessage)会唤醒线程;
Looper
的阻塞涉及到Linux pipe/epoll
机制
下面就看一下在消息机制里面是如何阻塞和唤醒的
3.2 MessageQueue中的next()
尝试检索下一条消息。如果找到则返回(没有消息则进入阻塞,释放cpu时间)
nativePollOnce(ptr, nextPollTimeoutMillis);
这个函数会让线程进入休眠状态
nextPollTimeoutMillis 为 -1 表示永久等待(直到被唤醒)/0表示不等待
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
//阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;//取到链表头
//检查链表里面是否存在异步消息 target==null代表这个消息是个异步消息屏障
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//获取到消息:可能是普通消息 也可能是异步消息
if (msg != null) {
//消息执行时间未到
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//获取到消息
// Got a message.
//把消息从链表中删除
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
//标记消息正在使用
msg.markInUse();
//返回这条消息
return msg;
}
} else {
// No more messages.
//链表里面没有消息,永久阻塞,等待唤醒
nextPollTimeoutMillis = -1;
}
............
mBlocked = true;
continue;//进入下一次循环
}
}
代码较长,其实总结就是
在next方法中,从队列中读取消息时,会先检查是否存在同步屏障。如果存在同步屏障,优先返回队列中的异步消息,并将该异步消息从队列中移除。如果队列中没有同步屏障,则返回队列中的同步消息,并将该同步消息从队列中移除
什么是同步和异步消息呢,首先看Message
3.3Message
其中两个重要属性
Handler target;//该消息的发送者
Message next;//消息是一个链表
public long when; //消息的执行时刻
消息对象还具有回收复用机制
在发送消息的时候 使用 handler.obtain 而不是直接 new Message()//他会先尝试从里面回去,取不到才new
loop 在处理完一条消息之后会调用msg.recycleUnchecked();来回收消息
private static Message sPool;//消息池链表
Message 内部维护了一个消息池链表,回收后的消息都会进入里面
消息还分为同步消息和异步消息
3.3.1同步消息
message中有一个重要的属性 when,表示该消息执行的时刻
Handler 的各种sendMessage方法 最终都会调用到
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//1.
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);
}
boolean enqueueMessage(Message msg, long when)
所以when 代表 这条消息什么时候执行(是一个时刻)
所以when 代表 这条消息什么时候执行(是一个时刻),例如有些消息需要延迟执行
- 可以根据这条消息什么时候执行吧消息插入到队列中合适的位置
- 如果队列为空或新消息的等待时间小于队头消息的等待时间,新消息将被放置在队列的头部。如果新消息的等待时间大于队头消息的等待时间,新消息将从队头开始依次与队列中的消息进行比较,直到找到合适的位置插入
这里有一个结论是,messageQueue里面的message会按照链表执行的时间顺序从链表头到链表尾排序,链表链表头的消息会最先执行,后面分析enqueueMessage会说到
3.3.2异步消息
异步指的是,他会优于其他普通消息执行,在他执行完毕之前其他消息无法执行
异步消息的逻辑也很简单
通过postSyncBarrier()
来创建一个屏障
本质上也是给链表中插入一条消息,但是这条消息的特殊之处在于这条message的traget也就handler为null
稍后通过removeSyncBarrier() 来移除掉屏障
这样通过message.setAsynchronous(true) 设置的异步消息就会优先执行
Android的刷新机制就是这样做的,这样可以有效的避免卡顿,毕竟视觉效果才是第一位嘛
大致来说呢就是 在 Vsync信号来的时候,设置同步屏障 ,之后 measure layout draw 绘制流程 包装成一个异步消息,执行完毕之后移除屏障
3.3 enqueueMessage
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
//消息标记为正在使用
msg.markInUse();
//设置执行时间
msg.when = when;
//下面的链表操作为按照时间顺序把message插入到链表合适的位置
Message p = mMessages;
boolean needWake;
//插入到链表头
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;//没有消息||当前消息需要立刻执行||当前消息比消息队列中任何一个消息都要早
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
//插入链表中
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
//插入消息之后如果是阻塞状态需要唤醒
nativeWake(mPtr);
}
}
return true;
}
}
3.4 idle Handler
idleHandler 和普通Handler不同,线程空闲的时候才会执行
具体到代码
//此时处于空闲状态
// 1.下一个 Message 执行时间未到
// 2.没有Message 了
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
// 把 mIdleHandlers 拷贝到 mPendingIdleHandlers 中
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//执行我们定义的逻辑
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//执行完毕之后是否删除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
使用方法
public class MyIdleHandler implements MessageQueue.IdleHandler {
@Override
public boolean queueIdle() {
// 在主线程空闲时执行任务
return false; // 返回 true 表示继续监听空闲事件,返回 false 表示不再监听
}
}
MyIdleHandler myIdleHandler = new MyIdleHandler();
Looper.myQueue().addIdleHandler(myIdleHandler);
// 在适当的时机注销
Looper.myQueue().removeIdleHandler(myIdleHandler);
3.5 handler 的dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {//执行Handler内部的callback
if (mCallback.handleMessage(msg)) {//返回false 的话handler Message 还会被调用
return;
}
}
handleMessage(msg);
}
}
类似责任链模式
如果msg有callBack 只处理message 的callback
否则 看Handler自己 有没有callback
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)