Android Handler 机制 屏障消息(同步屏障)

Handler Message 种类

Handler的Message种类分为3种:

  • 普通消息
  • 屏障消息
  • 异步消息

其中普通消息又称为同步消息,屏障消息又称为同步屏障。

我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障,在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。

MeeageQueue

消息同步屏障在MeeageQueue 中实现,下面我们来看看源码是如何做的。

一.添加消息屏障

private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //1、屏障消息和普通消息的区别是屏障消息没有tartget。
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            //2、根据时间顺序将屏障插入到消息链表中适当的位置
            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;
            }
            //3、返回一个序号,通过这个序号可以撤销屏障
            return token;
        }
}

 postSyncBarrier方法就是用来插入一个屏障到消息队列的,可以看到它很简单

1  屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。

2 屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。

3 postSyncBarrier返回一个int类型的数值,通过这个数值可以撤销屏障。

4 postSyncBarrier方法是私有的,如果我们想调用它就得使用反射。

二.移除消息屏障

public void removeSyncBarrier(int token) {
            // Remove a sync barrier token from the queue.  
            //....省略.......移除队列中barrier的token消息
            //唤醒线程
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

移除一个消息屏障,做了以下几件事:
1.移除次序列号的token消息
2.如果主线程是阻塞状态,则唤醒线程

三 取出消息屏障 

我们知道MessageQueue是通过next方法来获取消息的。

Message next() {
    //1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。
    nativePollOnce(ptr, nextPollTimeoutMillis);
    synchronized (this) {
        Message prevMsg = null;
        Message msg = mMessages;
        if (msg != null && msg.target == null) {//2、遇到屏障  msg.target == null
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());//3、遍历消息链表找到最近的一条异步消息
        }
        if (msg != null) {
            //4、如果找到异步消息
            if (now < msg.when) {//异步消息还没到处理时间,就在等会(超时时间)
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                //异步消息到了处理时间,就从链表移除,返回它。
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                msg.markInUse();
                return msg;
            }
        } else {
            // 如果没有异步消息就一直休眠,等待被唤醒。
            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();
        }
    }
  • 用框架中为了更快的响应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设置了同步屏障。

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值