金三银四,一文快速了解Android的Handler机制

消息机制

Android中的消息机制你可以简单地理解成于生产者------消费者模型。非UI线程是生产者,他们通过Handler添加Message到MessageQueue中,并唤醒消费者;主线程消费者被唤醒后,就通过Looper取出MessageQueue中Message来执行。完整流程如下所示:

这里有几点需要注意:

  1. 消息机制是如何让Message在主线程被消费的?

我们都知道在java中创建的对象内存是分配在堆中,而堆是线程共有的。因此。当我们在其他线程给MessageQueue添加Message时,主线程也可以获取添加的Message。当然,这里有一个可见性问题,不过Android中的处理也很简单,就是在 MessageQueue 的添加方法中使用了 synchronized 代码块,来确保可见性。

java 复制代码
//MessageQueue
boolean enqueueMessage(Message msg, long when) {  
    ...
    
    synchronized (this) {  
        ...
    }  
    return true;  
}
  1. 为什么我们给Handler设置的Callback和重写的handlerMessage方法也会被执行

这是因为Handler发送Message时,Message会带上Handler的引用。在Looper中处理Message时,最后会调到Handler的dispatchMessage。它先会判断msg是否有callback,如果有就执行msg的callback。如果没有就执行Handler的callback。如果handler的callback返回false,或者没有handler的callback,就执行handleMessage方法。

同步屏障

同步屏障是一种让异步Message提前处理的机制,目的是让特定的Message提前执行,比如ViewRootImpl中的scheduleTravelas 方法就会发送一个同步屏障。

当MessageQueue发现有同步屏障的消息时,其判断依据是msg.target 是否为null,就开始遍历执行异步Message,这样异步消息就先执行了。需要注意的是同步屏障不会主动移除,它会一直留在MessageQueue中,只能主动删除

java 复制代码
//MessageQueue
public int postSyncBarrier() {
    //默认when赋值的是开机时间运行时间
    return postSyncBarrier(SystemClock.uptimeMillis());
}

//Handler
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {  
    MessageQueue queue = mQueue;  
    if (queue == null) {  
        RuntimeException e = new RuntimeException(  
            this + " sendMessageAtTime() called with no mQueue");  
        Log.w("Looper", e.getMessage(), e);  
        return false;  
    } 
    //这里when赋值的是0
    return enqueueMessage(queue, msg, 0);  
}

不过实际上看源代码,同步屏障有一个问题,就是会被其他Message阻塞,它并不是优先级最高的。因此同步屏障消息不一定能及时执行到。这是因为同步屏障Message的when赋值的是开机时间运行时间,因此不是立即执行的。而Handler提供了sendMessageAtFrontOfQueue方法,它会把Message放在messageQueue链表头,实现方法就是设置Message的when为0。

IdleHandler

kotlin 复制代码
handler.looper.queue.addIdleHandler {  
    //在空闲时执行逻辑
    //返回值为false表明这个IdleHandler只需要执行一次,同时会被移除;为true时,则不移除
    return@addIdleHandler false  
}

IdleHandler是Android中提供的,在主线程空闲时执行逻辑的机制。

实现原理是:当有Message加入MessageQueue时,会唤醒在调用MessageQueue的next 方法时的阻塞。这时,他会查询Message 列表,看有没有需要现在执行的Message。如果没有,就会遍历 IdleHandler 的列表,并执行对应的方法。通过这种方式让IdleHandler在空闲时候执行。需要注意的是,在IdleHandler中最好不要执行耗时操作,不然会阻塞MessageQueue

内存泄漏

常见的就是Handler 泄漏;泄漏原因就是内部类引用了外部类,一般为activity。由于message 持有了 Handler,当message 延时很长时间比如说3分钟,这时就会发生内存泄漏问题。

参考

相关推荐
maycho12336 分钟前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此1 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql
brave_zhao1 小时前
达梦数据库(DM8)支持全文索引功能,但并不直接兼容 MySQL 的 FULLTEXT 索引语法
android·adb
sheji34161 小时前
【开题答辩全过程】以 基于Android的网上订餐系统为例,包含答辩的问题和答案
android
easyboot2 小时前
C#使用SqlSugar操作mysql数据库
android·sqlsugar
为码消得人憔悴2 小时前
Android perfetto - Perfetto 新手入门指南
android·性能优化
写代码的Eleven3 小时前
Rk3576 Andorid 14修改默认的通知音量,通话音量,闹钟音量等系统音量大小
android·framework
_李小白3 小时前
【Android FrameWork】延伸阅读:CursorWindow的作用
android
介一安全4 小时前
【Frida Android】实战篇14:非标准算法场景 Hook 教程
android·网络安全·逆向·安全性测试·frida
小虎牙0074 小时前
关于Android Compose架构的思考
android·前端·mvvm