Android源码分析之Handler同步屏障-《四》「建议收藏」

Android源码分析之Handler同步屏障-《四》「建议收藏」我们知道Handler可以发同步消息,但是大多数不知道怎么发送异步消息和其使用场景,甚至没听说过。随着在越来越多的面试中被频繁地问到,相信对于很

欢迎大家来到IT世界,在知识的湖畔探索吧!

我们知道Handler可以发同步消息,但是大多数不知道怎么发送异步消息和其使用场景,甚至没听说过。随着在越来越多的面试中被频繁地问到,相信对于很多人来说都是一头雾水,不知道Handler中还有同步屏障。今天让我们一起分析一下源码,揭开其神秘的面纱,也希望可以帮助大家在以后的面试中对答如流。

同步屏障顾名思义,就是先在消息队列中插入一个屏障,导致后面的普通消息都无法被处理,让异步消息优先处理。下面让我们结合源码一起学习。

首先我们来看一下怎么发送一个同步屏障:

/**向 Looper 的消息队列发布一个同步屏障。 消息处理照常进行
   *直到消息队列遇到已发布的同步障碍。 
   **/
 private int postSyncBarrier(long when) {
        // 插入一个新的同步屏障令牌.
        //我们不需要叫醒队列,因为设置屏障的目的是让队列暂停
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();//从消息池中获取一个消息体
            msg.markInUse();
            msg.when = when;   // 在消息体获取后并没有给msg.target赋任何值,
            msg.arg1 = token;  //所以 msg.target =null
         

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;  //如果屏障插入的时间不为0,且小于当前同步消息,则
                    p = p.next;  //将下一条消息给p
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;   //如果上一条消息不为null,则将屏障消息插入队列中
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

欢迎大家来到IT世界,在知识的湖畔探索吧!

通过上面这段代码分析,可以看出我们从消息池获取一个消息实体的时候,并没有对它的target属性进行赋值,此时msg的target=null,并且将整个target=null的消息插入消息队列中。这样我们就成功发送了一个屏障消息,至于异步消息是怎么被处理的,我们接着往下面看 。

我们都知道消息的处理都是调用Looper.loop()方法,然后调用MessageQueue的next方法进行消息的轮循处理。我们一起来看一下:

欢迎大家来到IT世界,在知识的湖畔探索吧!//MessageQueue.java  
@UnsupportedAppUsage
    Message next() {
       -------省略代码-----------
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
       -------省略代码-----------
           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) {
                    //插入屏障,然后在消息队列中找到下一条异步消息
                  do {
                        prevMsg = msg;
                        msg = msg.next;
                  } while (msg != null && !msg.isAsynchronous());
             //msg.isAsynchronous() 如果为true,则是异步消息,跳出循环,处理消息
              }
    -------省略代码-----------
    }

从上面代码分析可以看出,当发送一条屏障消息时(msg.target==null),消息会进入一个do-while循环,查找队列中的异步消息。直到找到 msg.isAsynchronous()为true的消息,退出循环,进行处理。这就保证了异步消息优先处理。

那么应用场景呢?我们接着往下看:

我们都知道整个安卓系统都是由Handler来管理消息的分发,即使是主线程也不例外,当屏幕进行刷新时,如果用户的消息没有在16毫秒内完成,那么就会造成屏幕卡顿,因此安卓系统要求所有的消息处理都必须在16秒内处理完成。但是如果消息队列中的消息很多,每一条消息处理花费十几毫秒,那就会造成严重卡顿。因此,系统为了保证正常运行,采用异步消息处理,向消息队列中发送一条屏障消息,当从消息队列取出消息前,优先判断消息体中是否包含消息屏障标志(target==null),然后阻塞后面的普通消息,优先处理异步消息来对UI进行及时地刷新。下面我们结合代码分析:

//系统源码 ViewRootImpl.java 2097行
    void scheduleTraversals() {
    if (!mTraversalScheduled) {
      mTraversalScheduled = true;
        //开启消息屏障
      mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
      //发送异步消息
      mChoreographer.postCallback(
         Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

接下来调用mChoreographer.postCallback 发送异步消息

欢迎大家来到IT世界,在知识的湖畔探索吧!//Choreographer.java. 462行

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
              //构建一条异步消息
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true); //设置异步消息标志
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

系统先通过发送一个屏障消息,保证了后面刷新UI的异步消息能够被优先处理。避免阻塞主线程。当发送完异步消息后,也会调用

//ViewRootImpl.java.  2108行 
void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除消息屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

总结:以上就行通过对系统源码的一步一步分析,我们可以清楚地知道什么是同步屏障,系统是怎么利用handler发送异步消息。系统源码博大精深,仍需要我们不断探索。

今天的问题是:1.系统是怎么移除同步屏障的?留给各位读者分析。如果有读者想对Handler其他部分感兴趣,可以阅读我的前三篇文章。至此Handler源码分析结束,接下来我会给大家准备更多的源码分析,希望对您有所帮助。如果大家有框架源码需要我帮忙分析的,欢迎在评论区留言。。

Android源码分析之Handler源码分析《一》

Android源码分析之Handler源码分析《二》

Android源码分析之Handler源码ThreadLocal分析《三》

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/16907.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信