Handler同步屏障机制的原理和使用场景

深入探讨Handler中的同步屏障机制,分析其工作原理及如何通过MessageQueue.postSyncBarrier设置,使Handler仅处理异步消息,实现消息优先级控制。
什么是Handler的同步屏障

Handler中的Message可以分为两类:同步消息、异步消息。消息类型可以通过以下函数得知

//Message.java
public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}

一般情况下这两种消息的处理方式没什么区别,在设置了同步屏障时才会出现差异。

Handler设置同步屏障之后可以拦截Looper对同步消息的获取和分发。加入同步屏障之后,Looper只会获取和处理异步消息,如果没有异步消息会进入阻塞状态。

怎样设置同步屏障

同步屏障可以通过MessageQueue.postSyncBarrier函数来设置

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) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

方法分析

可以看到,该函数仅仅是创建了一个 Message 对象并加入到了消息链表中。乍一看好像没什么特别的,但是这里面有一个很大的不同点是该 Message 没有 target

我们通常都是通过Handler发送消息的,Handler中发送消息的函数有postXXX、sendEmptyMessageXXX以及sendMessageXXX等函数,而这些函数最终都会调用enqueueMessage函数

//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    //...
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到 enqueueMessage 为 msg 设置了 target 字段。

所以,从代码层面上来讲,同步屏障就是一个 Message,一个 target 字段为空的 Message。

同步屏障的工作原理

同步屏障只在Looper死循环获取待处理消息时才会起作用,也就是说同步屏障在MessageQueue.next函数中发挥着作用。

next 函数相关代码

Message next() {
    //...

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        //...
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {//碰到同步屏障
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                // do while循环遍历消息链表
                // 跳出循环时,msg指向离表头最近的一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //...
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        //将msg从消息链表中移除
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    //返回异步消息
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            //...
        }

        //...
    }
}

可以看到,当设置了同步屏障之后,next函数将会忽略所有的同步消息,返回异步消息。换句话说就是,设置了同步屏障之后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

如何发送异步消息

通常我们使用 Handler 发消息时,这些消息都是同步消息,如果我们想发送异步消息,那么在创建 Handler 时使用以下构造函数中的其中一种( async传true )

public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

然后通过该Handler发送的所有消息都会变成异步消息

同步屏障的应用

Android4.1之后增加了Choreographer机制,用于同 Vsync 机制配合,统一动画、输入和绘制时机。

ViewRootImpl的requestLayout开启绘制流程:

	@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();//检查是否在主线程
            mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
            //重要函数
            scheduleTraversals();
        }
    }

Android 应用框架中为了更快的响应UI刷新事件在 ViewRootImpl.scheduleTraversals 中使用了同步屏障

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //设置同步障碍,确保mTraversalRunnable优先被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //内部通过Handler发送了一个异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

mTraversalRunnable 调用了 performTraversals 执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。

<think>我们正在讨论Android Handler机制中的同步消息异步消息的区分方法。根据之前的引用问题,我们需要从Handler、Message、MessageQueueLooper的角度来解释。 引用[2]提到:同步屏障是一种同步机制,用于确保某些操作按照预定的顺序执行,在Handler中是一种优先级策略,它会让异步消息的优先级高于同步消息。这是因为有些消息(如UI刷新)需要最高优先级,确保立即执行。 引用[3]说明了Handler机制的基本组成:Handler、Message、MessageQueueLooper。其中,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 机制同步消息与异步消息的区分方法 在 Android 的 Handler 消息机制中,同步消息异步消息的核心区分方式如下: --- #### **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、付费专栏及课程。

余额充值