从isAsynchronous方法来看看Handler异步消息与同步屏障(SyncBarrier)

前言

Android的消息机制之前有一篇文章有写,里面具体讲到了Handler怎么发送和处理消息的整个过程。感兴趣的同学可以先跳转过去看看 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)

在看消息机制的时候,不管是把消息加入队列,还是取出队列,Message有个isAsynchronous方法一直没关注,今天来看看这个方法到底是做什么的。

    /**
     * Returns true if the message is asynchronous, meaning that it is not
     * subject to {@link Looper} synchronization barriers.
     *
     * @return True if the message is asynchronous.
     *
     * @see #setAsynchronous(boolean)
     */
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }

Handler同步屏障(SyncBarrier)

要理解这个方法的含义,我们要先了解一下Handler的同步屏障机制。通常我们使用Handler发消息的时候,都是用的默认的构造方法生成Handler,然后用send方法来发送消息,其实这时候我们发送的都是同步消息,发出去之后就会在消息队列里面排队处理。我们都知道,Android系统16ms会刷新一次屏幕,如果主线程的消息过多,在16ms之内没有执行完,必然会造成卡顿或者掉帧。那怎么才能不排队,没有延时的处理呢?这个时候就需要异步消息,在处理异步消息的时候,我们就需要同步屏障,让异步消息不用排队等候处理。可以理解为同步屏障是一堵墙,把同步消息队列拦住,先处理异步消息,等异步消息处理完了,这堵墙就会取消,然后继续处理同步消息。

怎么来使用这个同步屏障呢?在MessageQueue里面有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) {
            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;
        }
    }

其实很简单,就是创建了一个消息,但是值得注意的是,这个消息没有target,普通的消息的必须有target的(不然交给谁来处理消息呢?具体的可以看开头的文章链接)。然后我们来看看怎么取出消息。

处理异步消息

来到MessageQueuenext方法:

    Message next() {
        省略代码
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            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 {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
            省略代码
        }
    }

这里可以看到有一个很关键的判断,上面我们知道屏障消息的target为空,所以这里判断为true,while循环直到取出异步消息终止。接下来的处理就跟同步消息一样了,这里不赘述。

怎么发送异步消息

那怎么来发送异步消息呢?MessagesetAsynchronous方法可以直接设置为异步消息

    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

还有就是可以看到Handler的构造方法

public Handler(boolean async)
public Handler(@Nullable Callback callback, boolean async)
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)

async参数可以控制是否发送异步消息,如果设置为true,Handler发送的都将是异步消息。

哪里有应用呢

我们平时要发送同步屏障postSyncBarrier需要反射才能使用

    public void postSyncBarrier() {
       Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
       token = (int) method.invoke(Looper.getMainLooper().getQueue());
   }

   public void removeSyncBarrier() {
       Method method = MessageQueue.class
    .     getDeclaredMethod("removeSyncBarrier", int.class);
        method.invoke(Looper.getMainLooper().getQueue(), token);}
   }

在Android系统里面为了更快响应UI刷新在ViewRootImpl.scheduleTraversals也有应用:

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

你可能感兴趣的:(从isAsynchronous方法来看看Handler异步消息与同步屏障(SyncBarrier))