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

参考

相关推荐
fanged2 小时前
天马G前端的使用
android·游戏
molong9316 小时前
Kotlin 内联函数、高阶函数、扩展函数
android·开发语言·kotlin
叶辞树8 小时前
Android framework调试和AMS等服务调试
android
慕伏白10 小时前
【慕伏白】Android Studio 无线调试配置
android·ide·android studio
低调小一10 小时前
Kuikly 小白拆解系列 · 第1篇|两棵树直调(Kotlin 构建与原生承载)
android·开发语言·kotlin
跟着珅聪学java10 小时前
spring boot 整合 activiti 教程
android·java·spring
川石课堂软件测试12 小时前
全链路Controller压测负载均衡
android·运维·开发语言·python·mysql·adb·负载均衡
2501_9159214312 小时前
iOS 26 电耗监测与优化,耗电问题实战 + 多工具 辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_9159214313 小时前
苹果软件混淆与 iOS 应用加固白皮书,IPA 文件加密、反编译防护与无源码混淆方案全解析
android·ios·小程序·https·uni-app·iphone·webview
倔强的石头10613 小时前
【Linux指南】Linux命令行进度条实现原理解析
android·linux