金三银四,一文快速了解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分钟,这时就会发生内存泄漏问题。

参考

相关推荐
Android-Flutter4 小时前
Compose 管理状态 详解
android·compose
缓慢更新5 小时前
审批的加签与会签:多人意见怎么合
android·java·数据库·低代码·rxjava
我怀里的猫6 小时前
android-activity-launch-sequence
android
YB13758 小时前
Jetpack Compose 动画rememberInfiniteTransition
android
baidu_2474386111 小时前
Android 自定义竖直的虚线
android
传奇开心果编程11 小时前
【Jetpack Compose娓娓道来】 第25课:ViewModel就是UI界面的大脑
android·kotlin·移动开发·jetpack compose·编程语言理论·编程框架·声明式ui
马立杰11 小时前
PHP 未授权 RCE 应急响应:CVE-2026-109618 CVE-2026-109619 排查与加固实战
android·开发语言·php
NamoAmitabha12812 小时前
安装“编译器环境”(Android Studio)--deepseek--Cocos --Creator3.8.8
android·ide·android studio
聚美智数12 小时前
文档转换 API‑文档转 PDF‑PDF 转文档‑异步文档处理‑文件格式迁移‑转换结果回调推送 API 接口介绍
android·java·pdf
booboosui13 小时前
Android 上的无人回合:只放开 2 类工具,宁可空手
android