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

参考

相关推荐
恋猫de小郭2 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin
aqi002 小时前
FFmpeg开发笔记(七十七)Android的开源音视频剪辑框架RxFFmpeg
android·ffmpeg·音视频·流媒体
androidwork4 小时前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
梦天20155 小时前
android核心技术摘要
android
szhangbiao6 小时前
“开发板”类APP如果做屏幕适配
android
高林雨露7 小时前
RecyclerView中跳转到最后一条item并确保它在可视区域内显示
android
移动开发者1号10 小时前
ReLinker优化So库加载指南
android·kotlin
山野万里__10 小时前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
Huckings10 小时前
Android 性能问题
android
移动开发者1号10 小时前
剖析 Systrace:定位 UI 线程阻塞的终极指南
android·kotlin