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

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

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 Handler机制中的同步消息和异步消息的区分方法。根据之前的引用和问题,我们需要从Handler、Message、MessageQueue和Looper的角度来解释。 引用[2]提到:同步屏障是一种同步机制,用于确保某些操作按照预定的顺序执行,在Handler中是一种优先级策略,它会让异步消息的优先级高于同步消息。这是因为有些消息(如UI刷新)需要最高优先级,确保立即执行。 引用[3]说明了Handler机制的基本组成:Handler、Message、MessageQueue和Looper。其中,MessageQueue是一个按触发时间排序的单向链表。 在Android消息机制中,消息分为三种类型:同步消息、异步消息同步屏障(一种特殊的消息)。我们关注的是同步消息和异步消息的区分方法。 区分方法: 1. 消息标记:在Message对象中有一个标志位`isAsynchronous`,用于标识该消息是否为异步消息- 同步消息:`isAsynchronous()`返回false。 - 异步消息:`isAsynchronous()`返回true。 2. 消息发送方式: - 同步消息:通常通过`Handler.sendMessage()`或`Handler.post()`发送的消息默认是同步消息- 异步消息:通过`Handler.postAtTime()`、`Handler.postDelayed()`等发送的消息默认也是同步消息。要发送异步消息,需要使用特定的方法: a. 在创建Handler时,指定Handler为异步的:通过`Handler(Handler.Callback callback, boolean async)`构造函数的第二个参数为true。 b. 使用Message的setAsynchronous(true)方法:在构造Message后,调用`message.setAsynchronous(true)`,然后发送。 3. 消息处理优先级(同步屏障): 当MessageQueue中存在同步屏障(通过`postSyncBarrier()`方法设置)时,它会阻止同步消息的处理,只允许异步消息被处理。同步屏障相当于一个特殊的消息,它的target(Handler)为null。在同步屏障被移除之前,同步消息都会被阻塞,而异步消息则不受影响。 4. 如何设置同步屏障同步屏障通常用于系统级的高优先级操作(如UI绘制)。我们可以通过`MessageQueue.postSyncBarrier()`方法设置,但此方法是隐藏的(@hide),一般应用开发者不会直接使用。移除同步屏障使用`MessageQueue.removeSyncBarrier(int token)`。 示例代码: 发送异步消息: ```java // 方法1:创建异步Handler Handler asyncHandler = new Handler(Looper.getMainLooper(), handlerCallback, true); // 第三个参数为true表示异步 // 方法2:使用Message的setAsynchronous方法 Message msg = Message.obtain(); msg.setAsynchronous(true); handler.sendMessage(msg); ``` 设置同步屏障(需要反射,不推荐在应用层使用): ```java // 获取MessageQueue MessageQueue queue = Looper.getMainLooper().getQueue(); Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier"); method.setAccessible(true); int token = (int) method.invoke(queue); // 设置同步屏障 // 移除同步屏障 Method removeMethod = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class); removeMethod.setAccessible(true); removeMethod.invoke(queue, token); ``` 注意:在应用开发中,我们通常不需要手动设置同步屏障,系统在需要的时候(例如ViewRootImpl中处理VSYNC信号时)会自动设置。而异步消息的使用也需谨慎,避免滥用导致消息处理顺序混乱。 总结区分方法: - 通过消息的`isAsynchronous()`标志位判断。 - 异步消息的发送需要显式设置(通过异步Handler或调用Message的setAsynchronous(true))。 -同步屏障存在时,只有异步消息会被处理,同步消息被阻塞。 引用[2]中提到同步屏障的存在是为了让异步消息(如UI刷新)优先执行,所以在需要及时响应的场景(如动画、UI更新)会使用异步消息。 相关问题: 1. 同步屏障Android系统中的应用场景有哪些? 2. 如何避免异步消息滥用导致的Handler消息队列混乱? 3. 在自定义Looper线程中如何处理同步消息和异步消息? 4. Handler消息机制如何保证线程安全? 注意:在回答中,我们需要在引用内容的末尾添加引用标识,例如[^2]。根据用户提供的引用,我们主要参考了引用[2]和引用[3]。 </think>### Android Handler 机制同步消息与异步消息的区分方法 在 AndroidHandler 消息机制中,同步消息和异步消息的核心区分方式如下: --- #### **1. 消息类型标识(核心区分点)** - **异步消息**:通过 `Message.setAsynchronous(true)` 显式标记 ```java Message asyncMsg = handler.obtainMessage(); asyncMsg.setAsynchronous(true); // 关键标识 handler.sendMessage(asyncMsg); ``` - **同步消息**:未显式设置异步标志的消息均为同步消息(默认类型) ```java // 默认创建的消息都是同步消息 Message syncMsg = handler.obtainMessage(); handler.sendMessage(syncMsg); ``` --- #### **2. 消息处理优先级(同步屏障机制)** 当存在 **同步屏障(Sync Barrier)** 时,两种消息的处理优先级不同: - **同步屏障**:通过 `MessageQueue.postSyncBarrier()` 设置的特殊标记节点 (系统在 VSYNC 信号触发 UI 渲染时自动设置)[^2] - **处理规则**: - 屏障存在时:优先处理**异步消息**(如 UI 刷新消息- 屏障移除后:按时间顺序处理**同步消息** ```mermaid graph LR A[同步屏障] --> B{消息队列} B --> C[异步消息] --> 优先执行 B --> D[同步消息] --> 暂停处理 ``` --- #### **3. 消息发送方式区分** | **特性** | 同步消息 | 异步消息 | |-------------------|-----------------------------------|-----------------------------------| | **Handler 创建** | `new Handler()` | `new Handler(true)` // 异步构造器 | | **标记方式** | 默认无标记 | `msg.setAsynchronous(true)` | | **系统使用场景** | 普通任务 | VSYNC 触发的 UI 刷新 [^2] | | **执行优先级** | 常规顺序 | 可突破同步屏障优先执行 | --- #### **4. 实战验证方法** 可通过拦截 `MessageQueue.next()` 验证消息类型: ```java // 反射获取 MessageQueue MessageQueue queue = Looper.getMainLooper().getQueue(); Field field = MessageQueue.class.getDeclaredField("mMessages"); field.setAccessible(true); // 检查消息类型 Message msg = (Message) field.get(queue); if (msg != null) { if (msg.isAsynchronous()) { Log.d("MSG_TYPE", "异步消息"); } else { Log.d("MSG_TYPE", "同步消息"); } } ``` --- #### **关键注意事项** 1. **异步消息慎用**:仅系统级高优先级任务(如 UI 刷新)需要异步消息,滥用会导致消息时序混乱 2. **屏障自动管理**:同步屏障由系统自动设置/移除(如 `ViewRootImpl` 处理 VSYNC 时) 3. **自定义异步 Handler**: ```java // 创建异步 Handler Handler asyncHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { // 处理异步消息 return true; } }, true // 关键异步标识 ); ``` --- ### 相关问题 1. 同步屏障的工作原理是什么?如何手动创建和移除同步屏障? 2. 在自定义 Looper 线程中如何处理异步消息? 3. 异步消息是否会影响 Android 应用的 UI 渲染性能? 4. Handler 消息机制中如何避免异步消息导致的时序错乱问题? [^1]: 通过 Handler 消息机制实现线程间通信保证 UI 流畅性 [^2]: 同步屏障确保异步消息优先级高于同步消息 [^3]: Android 消息机制Handler、MessageQueue、Looper 共同支撑
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值