源码分析->消息屏障是什么鬼?

源码分析基于Android-28
什么是消息屏障?

当要处理的消息为消息屏障,则延迟执行后续的普通消息,优先执行异步消息,常用在绘制任务的处理。消息屏障使得消息处理有了优先级。

Tips:有些博文说它是同步屏障,其实两者是同一个东西。
处理流程

如图所示,Msg1、Msg2、Msg3、Msg4均为普通消息,Msg5为异步消息。当在Msg1、Msg2之后插入一个消息屏障,那么Msg3、Msg4将会被延迟执行,Msg5优先执行。当消息屏障被移除,普通消息会得到执行机会。


处理流程.png
绘制任务处理流程
1.发送消息屏障

setContentView最终会调到以下方法,

ViewRootImpl.scheduleTraversals

主要工作:
(1)postSyncBarrier,往主线程队列发送一个SyncBarrier消息,也就是消息屏障,告诉主线程队列后续有绘制任务,普通消息延迟执行,消息屏幕是没有target的,这有别于普通消息;

Tips:插入消息屏障是不会唤醒线程的;

(2)mChoreographer.postCallback,注册绘制任务以及申请VSync信号;

Tips:VSync信号是SurfaceFlinger实现并定时发送;VSync信号监听器在是实例化mChoreographer成员变量mDisplayEventReceiver 的时候注册的;
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();          
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ......
        }
    }
Choreographer.postCallback最终调到postCallbackDelayedInternal

主要作用:
(1)mCallbackQueues[callbackType].addCallbackLocked,按时间先后顺序,将任务放入单链表中;

Tips:mCallbackQueues是一个长度为4的数组,每个元素都是一个单链表,保存跟输入、动画、绘制相关的任务;

(2)scheduleFrameLocked,申请下一次VSync信号。

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        ......
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
                      
   mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
scheduleFrameLocked(now);
 ......
        }
2.接收VSync信号
Choreographer.FrameDisplayEventReceiver.onVsync

主要工作:
(1)当VSync屏幕刷新信号到来会触发该方法,方法发送了一个异步消息到主线程队列,任务就是自己本身,这个任务就是需要优先执行的。
(2)doFrame,回调到上述postCallback mTraversalRunnable的run方法,最终会执行ViewRootImpl.doTraversal;

@Override
     public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            ......
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
Choreographer.doFrame

主要工作:
有个很重要的Log信息,jitterNanos为该帧延迟执行的时间,时间定于16毫秒且延迟30帧,则打印出Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");

void doFrame(long frameTimeNanos, int frame) {
          .......
            long intendedFrameTimeNanos = frameTimeNanos;//预期执行时间
            startNanos = System.nanoTime();//当前时间
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
          ......
    }
3.处理消息屏障
MessageQueue.next

主要工作:
(1)for循环,不断地取消息出来处理;
(2)当消息不为空且消息target为空,则是消息屏障,则从后续消息中取出异步消息来进行处理;
(3)如果异步消息还没有到时间,则休眠等待;
(3)如果异步消息到时间,则把消息返回;

Message next() {
            ......
            synchronized (this) {
            ......
                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());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        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;
                    }
                  ......
    }

上回消息屏障会在绘制任务执行的ViewRootImpl.doTraversal方法中移除,这样就能普通消息正常执行。

补充:Choreographer

该类中文名字编舞者,它负责通过内部类的父类DisplayEventReceiver.scheduleVsync注册VSync信号,通过内部类FrameDisplayEventReceiver.onVsync接收信号。

总结

(1)ViewRootImpl发消息屏障到主线程消息队列,Choreographer把绘制任务放入队列,并申请VSync信号;
(2)Choreographer的内部类FrameDisplayEventReceiver提供接收VSync信号以及申请VSync信号的方法;
(3)当FrameDisplayEventReceiver接收到VSync信号,往主线程发送异步消息,消息任务就是自己本身;
(4)MessageQueue取到异步消息进行处理;
(5)FrameDisplayEventReceiver.run方法被执行,遍历绘制任务队列,以此执行;
(6)最后移除消息屏障;
(7)VSync信号是SurfaceFlinger实现并定时发送;

以上分析有不对的地方,请指出,互相学习,谢谢哦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354