2.2.1 Handler消息驱动机制

本文深入剖析Android Handler消息驱动机制,涉及MessageQueue、Looper、Handler的交互,以及消息的同步屏障、内存管理、线程安全和延迟消息处理。重点讨论了Handler如何避免内存泄漏,以及在主线程中如何保证消息的及时处理,防止ANR的发生。同时,解释了Message的创建方式和IdleHandler的使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Handler消息驱动机制

消息就是Runnable和Message,Runnable也会被装成Message,本质上都是Message。
所有的消息放入MessageQueue中,消息队列并非队列,是一个链表。
Looper不断从MessageQueue中取出数据,交给Handler处理。

MessageQueue是一个基于消息触发时间的优先级队列,还有一个基于此消息队列的事件循环 Looper,Looper 通过循环,不断的从 MessageQueue 中取出待处理的 Message,再交由对应的事件处理器 Handler/callback 来处理。
每个线程只有一个MessageQueue和Looper,当Handler创建时与它的线程绑定。

在Looper.prepare里得到ThreadLocal<Looper>,调用Looper的构造方法创建Looper,
Looper构造方法中创建MessageQueue,获得Thread
通过Looper.loop:启动循环,调用queue.next获取消息,message.target.dispatchMessage处理消息
使用Handler有2种场景
1在将来执行某件事(Runnable/Message)
2在某个线程执行某件事(Runnable/Message)e.g.在主线程更新ui
Handler的职责是分发消息和处理消息。(dispatchMessage,handleMessage)
同步屏障:
Message分两种:同步消息、异步消息。
通过MessageQueue.postQueue()可以设置同步屏障 --> 其实就是插入一条没有target的Message。
next()循环中如果碰到同步屏障,会遍历链表找到最近的一个异步消息并返回。
应用:为了更快响应UI刷新事件,在scheduleTraversals时使用到了内存屏障。

FAQ

ThreadLocal

threadlocal

postDelayed延迟消息如何处理

计算message要被处理时间点(时间戳+delay)保存到message.when,
在enqueue时根据when从小到大构建链表。
我们知道loop()通过queue.next()获取message,
next()方法的循环中,获取消息根据时间戳和when判断时间有没有到,如果没到计算阻塞时间,在进入下轮循环之前阻塞。

Handler 引起的内存泄露原因以及最佳解决方案

用Handler处理延迟消息,Activity关闭后,handler作为内部类的话,仍然持有对外部类引用,Activity泄漏。

将 Handler 定义成静态的内部类,在内部持有Activity的弱引用,并在Acitivity的onDestroy()中调用handler.removeCallbacksAndMessages(null)及时移除所有消息。

Handler内存泄漏的引用链:
主线程 —> threadlocal —> Looper —> MessageQueue —> Message —> Handler —> Activity

https://www.cnblogs.com/jimuzz/p/14187408.html

Looper死循环为什么不会导致应用卡死

主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
造成ANR的不是主线程阻塞,而是主线程的Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新UI。
阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处理,程序是不会无响应的。
(可引出ANR相关知识)

消息循环没有消息以后怎么处理的

在通过MessageQueue的next()方法获取Message时,如果没有消息,会让线程进入等待状态(nativePollOnce() ),等待有消息时被唤醒(nativeWake())。

Message如何创建

1直接生成实例Message m = new Message
2通过Message m = Message.obtain
3通过Message m = mHandler.obtainMessage()
后两者效果更好,因为Android默认的消息池中消息数量是10,而后两者是直接在消息池中取出一个Message实例,这样做就可以避免多生成Message实例。

IdleHandler

IdleHandler 是一个接口

// MessageQueue.java 文件中
public static interface IdleHandler {
  boolean queueIdle();
}

IdleHandler 在 MessageQueue 出现空闲的时候被执行,那么何时出现空闲?

  1. MessageQueue 为空,没有消息;
  2. MessageQueue 中最近需要处理的消息,是一个延迟消息(when>currentTime),需要滞后执行;
    这两个场景,都会尝试执行 IdleHandler。
// 使用
Looper.myQueue().addIdleHandler(...);

native的epoll机制

这个文章 https://juejin.cn/post/6896495861954510861

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值