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

参考

相关推荐
郑州光合科技余经理21 分钟前
同城小程序开发:同一笔单在用户端和商家端各显示到哪
android·java·开发语言·前端·后端·uni-app·php
pigdreams2 小时前
Google 官方 Android CLI:让AI直接读布局结构而不是用adb截图验证
android·人工智能·adb
hunterandroid4 小时前
[Android 从零到一] Glide 图片加载原理:三级缓存、生命周期绑定与内存优化
android
法的空间4 小时前
AI 编程时代,为什么 Dart 分析会拖慢大型项目的开发
android·flutter·ios
鱼儿也有烦恼5 小时前
客户端应用架构模式
android·架构
独隅5 小时前
Claude 高效应用实战指南
android·java·开发语言·claude·claude code
硅基手札5 小时前
Android 系统全景专栏 00 · 总目录
android·操作系统·嵌入式开发
YB13756 小时前
Jetpack Compose 动画Crossfade
android
2401_833269306 小时前
Android歌词功能实现
android
恋猫de小郭6 小时前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter