Android中Handler的消息机制--同步屏障

本文解析了Android Handler中的同步屏障机制,介绍了屏障消息如何阻止普通消息执行,以确保异步消息优先级,并通过示例演示了如何插入、删除屏障消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ps:阅读原文可读性会好一些,原文链接:https://mp.weixin.qq.com/s/RIgyO85mFO5ZVXLXEdF9OA

前面我们花了很长时间用两篇文章(Android中Handler的消息机制分析(一)和Android中Handler的消息机制分析(二))分析 Handler 的消息机制,这一篇我们来分析 Handler 消息机制中的一种功能叫同步屏障;Message 可分为3种:普通消息、屏障消息和异步消息,其中普通消息又叫同步消息,屏障消息又叫同步屏障;屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理,屏障不会挡住异步消息;屏障消息的目的是确保异步消息的优先级,让异步消息先执行。

我们看看 MessageQueue 的 next 方法,看看哪些是屏障消息;

Message next() {

for (;😉 {

synchronized (this) {

Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}

}

}
}

在 MessageQueue 的 next 方法中,获取当前将要执行的消息时,对 Message 的 target(属于 Handler 类型) 字段进行了判断,如果当前 Message 的 target 为空,那么当前的 Message 是屏障消息,则屏蔽当前的 Message 以及后续所有同步消息的执行,异步消息不受影响,可能这样描述不是很让人理解;我举个例子,假设 msg1 是屏障消息,msg1 的下一个消息 msg2 是同步消息,msg2 的下一个消息是 msg3 是异步消息,那么 msg1 和 msg2 就不会执行,msg3 就会执行;当 !msg.isAsynchronous() 为 true 时,msg 就是同步消息。

添加同步屏障的操作在 MessageQueue 的 postSyncBarrier 方法,它在 UI 的绘制方面有用到,了解更多 UI 绘制过程中进行消息屏障,可参考对Android中View的post方法进行探索这篇文章,我们且看 postSyncBarrier 方法;

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {

        //1、
        final int token = mNextBarrierToken++;

        //2、
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;

        //3、
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }

        //4、
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

postSyncBarrier 方法调用了参数类型为 long 的 MessageQueue.postSyncBarrier 方法,该参数表示的是 MessageQueue 的 next 方法取出消息的时间;注释1 表示 mNextBarrierToken 属性获取一个 int 类型的 token,它是该方法的返回值,当移除屏障时会使用到 token;注释2 表示创建一个屏障消息,为什么说是屏障消息呢?因为这个消息的 target 属性为空,看到没有,我们这个 postSyncBarrier 方法里面没有给 消息的 target 属性赋值;注释3 中的 while 循环表示根据 when 属性,将同步屏障消息从消息队列中找到合适的位置;注释4 中的 if 语句里的代码表示根据 when 属性,将同步屏障消息添加到消息队列的合适位置。

有了添加屏障消息的操作那肯定也有删除屏障消息的操作,它的具体实现是在 MessageQueue 的 removeSyncBarrier 方法里;

public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;

        //5、
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }

        //6、
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;

        //7、
        if (prev != null) {
            prev.next = p.next;
            needWake = false;

            //8、
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

MessageQueue 的 removeSyncBarrier 方法中的 token 参数要必须是我们添加屏障消息的返回值 token,否则删除屏障消息就会失败;注释5 表示先判断当前消息 Message,是否是同步屏障消息,如果当前消息不是同步屏障消息,则遍历消息链表进行查找;注释6 表示如果没有找到我们指定要删除的屏障消息,就抛出异常;注释7 表示如果屏障消息 p 还未执行,则删除该消息,不用执行唤醒取消息的线程;当前消息是屏障消息 p,那么删除该消息后,还需要根据条件,进一步判断是否需要唤醒取消息的线程。

好了,分析了那么多,我们来写一个 demo 来体验一下吧,这里我们的 demo 功能有插入屏障消息、插入普通消息、插入异步消息和移除屏障消息(注意:demo 最小的 API 是 23)。

(1)新建一个 kotlin 语言类型的 Activity,名叫 MainActivity:

class MainActivity : AppCompatActivity() {
companion object {
var TAG: String = “MainActivity”
var HANDLE_SYNCHRONOUS_MESSAGE: Int = 1
var HANDLE_ASYNCHRONOUS_MESSAGE: Int = 2
var EXIT: Int = 3
}

var token: Int = -1
var mH: Handler? = null
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    MyT().start()
}

fun onClick(v: View) {
    when (v.id) {
        R.id.btn_1 -> {
            sendMessage("插入一条普通消息",HANDLE_SYNCHRONOUS_MESSAGE,false)
        }
        R.id.btn_2 -> {
            sendMessage("插入一条异步消息",HANDLE_ASYNCHRONOUS_MESSAGE,true)
        }
        R.id.btn_3 -> {
            postSyncBarrier();
        }
        R.id.btn_4 -> {
            removeSyncBarrier();
        }
    }
}

fun sendMessage(optinMessage: String,what: Int,asynchronous: Boolean) {
    Log.d(TAG,optinMessage)
    var msg: Message = Message.obtain();
    msg.what = what
    msg.isAsynchronous = asynchronous
    if (mH != null) {
        mH!!.sendMessage(msg)
    }
}

fun postSyncBarrier() {
    Log.d(TAG,"插入同步屏障")
    if (mH != null) {
        var mq: MessageQueue = mH?.getLooper()!!.queue
        var method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier")
        method.isAccessible = true
        token = (method.invoke(mq)) as Int
    }
}

fun removeSyncBarrier() {
    if (token != -1) {
        Log.d(TAG,"移除同步屏障")
        if (mH != null) {
            var mq: MessageQueue = mH?.getLooper()!!.queue
            var method: Method = MessageQueue::class.java.getDeclaredMethod("removeSyncBarrier",Int::class.java)
            method.isAccessible = true
            method.invoke(mq,token)
        }
        token = -1
    }
}

override fun onPause() {
    super.onPause()
    if (mH != null) {
        mH!!.sendEmptyMessage(EXIT)
    }
}

override fun onDestroy() {
    super.onDestroy()
    if (mH != null) {
        mH!!.removeCallbacksAndMessages(null)
        mH = null
    }
}

inner class MyH(looper: Looper) : Handler(looper) {
    override fun handleMessage(msg: Message?) {
        super.handleMessage(msg)
        when (msg?.what) {
            HANDLE_SYNCHRONOUS_MESSAGE -> {
                Log.d(TAG, "处理同步消息");
            }
            HANDLE_ASYNCHRONOUS_MESSAGE -> {
                Log.d(TAG, "处理异步消息");
            }
            EXIT -> {
                Looper.myLooper().quit()
            }
        }
    }
}

inner class MyT : Thread() {
    override fun run() {
        super.run()
        Looper.prepare()
        mH = MyH(Looper.myLooper())
        Looper.loop()
    }
}

}

(2)新建 MainActivity 对应的布局文件 activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>






程序运行时,界面展示如下所示:

图片

当我点击 “插入一条屏障消息” 按钮后再点击 “插入一条普通消息” 按钮,日志输出如下所示,发现没有看到 MyH 处理普通消息;

10-05 22:54:46.678 13496-13496/com.xe.handlerdemo3 D/MainActivity: 插入同步屏障
10-05 22:55:01.336 13496-13496/com.xe.handlerdemo3 D/MainActivity: 插入一条普通消息

当我继续点击 “插入一条异步消息” 按钮后,日志打印如下所示,发现 MyH 处理了异步消息;

10-05 22:57:43.228 13496-13496/com.xe.handlerdemo3 D/MainActivity: 插入一条异步消息
10-05 22:57:43.229 13496-13661/com.xe.handlerdemo3 D/MainActivity: 处理异步消息

当我继续点击 “移除屏障消息” 按钮时,日志打印如下所示,发现 MyH 处理了之前没有处理的普通消息;

10-05 23:05:01.587 16365-16400/com.xe.handlerdemo3 D/MainActivity: 处理同步消息

本篇文章写到这里就结束了,由于技术水平有限,文章中难免会出错,欢迎大家批评指正。

<think>嗯,用户之前让我介绍了Android Framework的整体理解,现在又要求详细讲解Handler消息机制及其三角模型。看来他对Android消息处理机制比较感兴趣,可能是在开发中遇到了相关的问题,或者想深入理解这一部分。 首先,我需要回想一下Handler消息机制的基本组成。用户提到了“三角模型”,也就是MessageQueue、Looper和Handler之间的关系。这三个组件协同工作,处理线程间的通信,特别是主线程和子线程之间的消息传递。可能用户在使用过程中遇到了线程切换的问题,或者想要优化消息处理的效率。 接下来,我应该考虑用户可能的实际应用场景。比如,在Android开发中,Handler常用于更新UI,因为主线程不能执行耗时操作,所以子线程完成任务后通过Handler发送消息到主线程更新界面。用户可能在使用时遇到了消息延迟、内存泄漏或者ANR(应用无响应)的问题,需要理解其原理来避免这些问题。 然后,我需要详细分解每个组件的作用。MessageQueue作为消息队列,存储待处理的消息;Looper不断循环从队列中取出消息Handler负责发送和处理消息。需要强调Looper如何通过ThreadLocal绑定到特定线程,确保每个线程只有一个Looper实例,这可能是用户容易忽略的部分,导致出现多个Looper的错误。 此外,还要注意消息的异步处理,比如使用postDelayed方法延迟执行任务,或者区分同步消息和异步消息(setAsynchronous)。用户可能在处理消息优先级或延迟任务时有疑问,需要解释消息屏障(Barrier)的作用和同步屏障机制。 可能用户对消息处理的机制理解不够深入,导致代码中出现内存泄漏,比如Handler持有Activity的引用,而消息队列中仍有未处理的消息,导致Activity无法被回收。这时候需要提到使用静态内部类和弱引用来避免内存泄漏。 另外,关于Looper的死循环不会导致ANR的问题,用户可能会有疑问,需要解释主线程的Looper在等待消息时会释放CPU资源,而只有当没有消息处理时才会进入阻塞状态,不会一直占用CPU,从而不会导致ANR。ANR通常是因为主线程处理消息耗时过长,而不是Looper本身的问题。 最后,总结Handler机制Android开发中的重要性,比如保证线程安全、实现任务调度和跨线程通信等。用户可能需要这些信息来优化自己的代码结构,或者解决实际开发中的问题。 综上所述,我需要从组成结构、工作原理、核心源码、使用要点和常见问题几个方面来详细讲解,确保用户不仅理解概念,还能应用到实际开发中,避免常见错误。</think>$$Android Handler消息机制深度解析$$ 我将从消息机制的架构模型、核心源码实现、使用场景及常见问题四个维度进行系统阐述: $$一、三角模型结构拓扑$$ ``` ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Message │ │ Looper │ │ Handler │ │ Queue │◄─────►│ (轮询器) │◄─────►│ (处理器) │ └───────────┘ └───────────┘ └───────────┘ ▲ ▲ ▲ │ 消息存储 │ 消息分发 │ 消息处理 │ │ │ ``` $$二、核心组件工作原理$$ 1. **MessageQueue(消息队列)** - 数据结构:单链表(按when时间排序) - 关键方法: ```java boolean enqueueMessage(Message msg, long when) // 入队操作 Message next() // 出队操作(可能阻塞) ``` - 同步屏障机制:通过`postSyncBarrier()`插入屏障消息,优先处理异步消息 2. **Looper(消息泵)** - 线程绑定:通过`ThreadLocal<Looper>`实现线程隔离 - 核心循环: ```java public static void loop() { for (;;) { Message msg = queue.next(); // 可能阻塞 msg.target.dispatchMessage(msg); // 分发处理 } } ``` - 唤醒机制:通过epoll监控文件描述符,避免CPU空转 3. **Handler消息处理器)** - 消息构造:提供`obtainMessage()`复用消息对象 - 发送方式: ```java sendMessage(Message) // 立即发送 postDelayed(Runnable, delay) // 延迟发送 ``` - 处理策略: ```java public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); // 执行Runnable } else { handleMessage(msg); // 子类重写处理 } } ``` $$三、源码级交互流程示例$$ 以发送消息到主线程为例: ```java // 子线程发送 new Thread(() -> { Message msg = handler.obtainMessage(WHAT_CODE, data); handler.sendMessage(msg); }).start(); // 主线程处理 Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 更新UI } }; ``` $$四、关键参数与性能指标$$ | 参数项 | 典型值/说明 | 优化要点 | |-----------------|----------------------------|----------------------------| | 消息延迟精度 | ±10ms(受系统调度影响) | 避免依赖精确时序逻辑 | | 单队列容量 | 默认50条(超过触发警告) | 及时移除无用消息 | | 消息处理耗时阈值 | 超过16ms可能导致丢帧 | 复杂任务拆分到子线程 | | 内存占用 | 每个Message约40字节 | 使用消息池(obtainMessage) | $$五、常见问题解决方案$$ 1. **内存泄漏** ```java // 错误示例:匿名Handler持有Activity引用 new Handler().postDelayed(() -> updateUI(), 10000); // 正确写法:使用静态内部类+弱引用 static class SafeHandler extends Handler { WeakReference<Activity> mActivity; SafeHandler(Activity activity) { mActivity = new WeakReference<>(activity); } //...处理消息时检查activity是否存在 } ``` 2. **ANR触发条件** - 主线程Handler处理消息耗时超过5秒 - 解决方案: ```java // 将耗时操作转移到工作线程 handler.post(() -> { // 快速预处理 new Thread(() -> { Object result = doHeavyWork(); handler.post(() -> updateUI(result)); }).start(); }); ``` 3. **同步屏障应用场景** ```java // 插入同步屏障(API 23+) ViewRootImpl.getRunQueue().postSyncBarrier(); // 发送异步消息 Handler asyncHandler = new Handler(Looper.getMainLooper()) { public Handler(Callback callback, boolean async) { super(callback, async); } }; asyncHandler.setAsynchronous(true); ``` $$六、高级应用技巧$$ 1. **消息优先级控制** ```java Message msg = handler.obtainMessage(); msg.setAsynchronous(true); // 标记为高优先级 handler.sendMessageAtFrontOfQueue(msg); ``` 2. **IdleHandler应用** ```java Looper.myQueue().addIdleHandler(() -> { // 主线程空闲时执行 return false; // true表示保持监听 }); ``` 3. **跨进程消息传递** ```java // 通过Messenger封装Handler Messenger messenger = new Messenger(handler); IBinder binder = messenger.getBinder(); // 通过Binder传递到其他进程 ``` 该机制的设计充分体现了生产者-消费者模式的应用,其核心价值在于: 1. 实现线程安全的通信管道 2. 提供精确的时间调度能力 3. 构建松耦合的组件交互模式 4. 支撑Android整个事件驱动架构 理解Handler机制需要重点关注以下三点: - **线程本地存储**(ThreadLocal实现机制- **消息队列同步**(synchronized与native方法配合) - **延迟消息计算**(SystemClock.uptimeMillis()基准时间)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值