• Android源码分析之Message


      准备开始写点东西,算是对自己阅读源码的一个记录/笔记,也希望能对同样感兴趣的人有所帮助,希望能坚持下去,加油。

      在Android的开发中,我们经常用到Handler.postXXX方法,或者View.postXXX方法,用来在下一次looper到来时执行。

    我是那样的人,什么事情最好能够知道下内部实现机理是什么,否则我在用它的时候可能会觉得不爽,或者说不自然,不太愿意去用。

    典型例子就是我始终不太愿意用Android引入的SparseArray<E>,而是一直坚持Java的HashMap<Key, Value>,直到我自己读了

    SparseArray<E>的源码,才开始放心大胆的使用(后面会写一篇文章专门分析它)。

      首先来看下面的代码:

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            // do something...
            removeCallbacks(this);
        }
    };
    
    postDelayed(mRunnable, SOME_DELAY_IN_MILLIMS);

    第一直觉告诉我run方法里的removeCallbacks(this);调用显然是多余的。通读代码发现的确是如此,因为任何Message

    (即使post的是Runnable也会被包装到Message里)在被处理之前都已经从MessageQueue里取出来了(delete掉了,所以客户端

    代码大可不必有这样的代码)。这里顺便提下慎用View.removeCallbacks的返回值,看源码:

     1 /**
     2      * <p>Removes the specified Runnable from the message queue.</p>
     3      *
     4      * @param action The Runnable to remove from the message handling queue
     5      *
     6      * @return true if this view could ask the Handler to remove the Runnable,
     7      *         false otherwise. When the returned value is true, the Runnable
     8      *         may or may not have been actually removed from the message queue
     9      *         (for instance, if the Runnable was not in the queue already.)
    10      *
    11      * @see #post
    12      * @see #postDelayed
    13      * @see #postOnAnimation
    14      * @see #postOnAnimationDelayed
    15      */
    16     public boolean removeCallbacks(Runnable action) {
    17         if (action != null) {
    18             final AttachInfo attachInfo = mAttachInfo;
    19             if (attachInfo != null) {
    20                 attachInfo.mHandler.removeCallbacks(action);
    21                 attachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
    22                         Choreographer.CALLBACK_ANIMATION, action, null);
    23             } else {
    24                 // Assume that post will succeed later
    25                 ViewRootImpl.getRunQueue().removeCallbacks(action);
    26             }
    27         }
    28         return true;
    29     }

    我们可以看到这个方法always返回true,所以不要基于它的返回值做任何事情,还有它的返回值的意义也需要格外留意下。

    我在第一次看到这个方法时就自以为然的觉得返回值肯定代表了Runnable action有没有成功地从MessageQueue中移除,true代表成功

    移除了,false代表移除失败,呵呵,你想错了。仔细看看方法的doc,人家说的是return true表示这个view可以让它的Handler

    去处理这件事情,并没提及处理的结果,而且即使返回true的时候也不能说明Runnable就已经从MessageQueue中移除了,

    比如说此时Runnable已经不在MessageQueue中了;其他情况都是返回false。这里顺便看眼View.postDelayed方法:

    1 public boolean postDelayed(Runnable action, long delayMillis) {
    2         final AttachInfo attachInfo = mAttachInfo;
    3         if (attachInfo != null) {
    4             return attachInfo.mHandler.postDelayed(action, delayMillis);
    5         }
    6         // Assume that post will succeed later
    7         ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
    8         return true;
    9     }

    这里不论是postDelayed还是removeCallbacks方法都首先检查了自己的mAttachInfo,如果非空才delegate给attachInfo的Handler

    处理,所以你尽量不要过早(mAttachInfo还没初始化完毕)的调用这些方法。比如在早期版本的Android中如果你过早的调用post,

    runnable不会被执行,参考这个问题:

    http://stackoverflow.com/questions/4083787/runnable-is-posted-successfully-but-not-run;

    感兴趣的同学可以在google中搜索view post runnable not run或者自行验证。我现在分析的是Android4.4的源码,依现在的代码来看

    即使mAttachInfo是null,也会执行ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); 所以可能在较新的平台上不是

    问题(有待考证)。经测试验证确实没问题,即使在Activity.onCreate中调用View.postXXX方法,runnable还是会被执行。

      好了说了一大堆了,开始正题。作为开始我今天挑了一个最简单的开始分析,那就是Message.java文件。

      其实说白了,Message就是一个数据类,持有data的。基本的数据字段我就不介绍了,都能望文生义。看下几个我觉得有必要的字段:

     1  /*package*/ Handler target;
     2 
     3     /*package*/ Runnable callback;
     4 
     5     // sometimes we store linked lists of these things
     6     /*package*/ Message next;
     7 
     8     private static final Object sPoolSync = new Object();
     9     private static Message sPool;
    10     private static int sPoolSize = 0;
    11 
    12     private static final int MAX_POOL_SIZE = 50;

    target是消息的处理者,在以后Looper.loop()方法中Message被从MessageQueue取出来后会调用msg.target.dispatchMessage(msg);

    callback是消息要执行的动作action。这里提前插播下Handler的dispatchMessage方法:

     1 public void dispatchMessage(Message msg) {
     2         if (msg.callback != null) {
     3             handleCallback(msg);
     4         } else {
     5             if (mCallback != null) {
     6                 if (mCallback.handleMessage(msg)) {
     7                     return;
     8                 }
     9             }
    10             handleMessage(msg);
    11         }
    12     }

    我们可以看到Handler在分发消息的时候,Message自身的callback优先级高,先被调用如果非空的话(callback的run方法直接被调用)。

    next表示消息队列中的下一个Message,类似单链表的概念。

    剩下的pool相关的字段都是Message引入的重用(reuse)所要用到的变量,sPoolSync是对象锁,因为Message.obtain方法会在任意

    线程调用;sPool代表接下来要被重用的Message对象;sPoolSize表示有多少个可以被重用的对象;MAX_POOL_SIZE显然是pool的上限,

    这里hardcode是50。这里我要分析的就2个方法,

    obtain和recycle,代码如下:

     1 /**
     2      * Return a new Message instance from the global pool. Allows us to
     3      * avoid allocating new objects in many cases.
     4      */
     5     public static Message obtain() {
     6         synchronized (sPoolSync) {
     7             if (sPool != null) {
     8                 Message m = sPool;
     9                 sPool = m.next;
    10                 m.next = null;
    11                 sPoolSize--;
    12                 return m;
    13             }
    14         }
    15         return new Message();
    16     }
    17 
    18 /**
    19      * Return a Message instance to the global pool.  You MUST NOT touch
    20      * the Message after calling this function -- it has effectively been
    21      * freed.
    22      */
    23     public void recycle() {
    24         clearForRecycle();
    25 
    26         synchronized (sPoolSync) {
    27             if (sPoolSize < MAX_POOL_SIZE) {
    28                 next = sPool;
    29                 sPool = this;
    30                 sPoolSize++;
    31             }
    32         }
    33     }

    首先我们来看obtain方法,第一次调用也就是说没有什么东西可以重用,这时sPool是null,直接new一个Message对象返回,等到

    Message对象使用完毕(在Looper.loop方法最后有msg.recycle();这样的代码),它的recycle会被调用,在recycle里首先会调用

    clearForRecycle方法,它只是把各个字段置空或清零。接下来sPoolSize没到上限,next保存下sPool的旧值(也就是在当前Message

    回收利用之前上一个要被回收利用的对象),然后sPool被更新成新值,即当前Message,sPoolSize加1,表示又多了一个可以重用的

    Message对象。之后在等到obtain被调用的时候就不是直接return一个new Message了,因为我们已经有可以重用的Message对象了。

    将sPool的值设置给我们要返回的Message m对象,接着sPool被更新成上一个要被重用的Message对象(相比recycle是反向过程),

    最后设置m的next字段为空(m.next会在重新入队列的时候被设置成合适的值),相应的sPoolSize减1,表示可重用的对象少了一个,

    最后返回重用的对象m。

      基于有这么个回收再利用的机制,Android建议我们调用Message的obtain方法来获取一个Message对象,而不是调用ctor,因为很

    可能会省掉分配一个新对象的开销。

      到目前为止,我们好像忽略了Message的一个(重要)方面,即异步性,代码如下:

      /**
         * Sets whether the message is asynchronous.
         *
         * Asynchronous messages represent interrupts or events that do not require global ordering
         * with represent to synchronous messages.  Asynchronous messages are not subject to
         * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
         *
         * @param async True if the message is asynchronous.
         *
         * @see #isAsynchronous()
         * @see MessageQueue#enqueueSyncBarrier(long)
         * @see MessageQueue#removeSyncBarrier(int)
         *
         * @hide
         */
        public void setAsynchronous(boolean async) {
            if (async) {
                flags |= FLAG_ASYNCHRONOUS;
            } else {
                flags &= ~FLAG_ASYNCHRONOUS;
            }
        }

    看方法的doc,我们知道异步的Message表示那些不需要全局顺序的中断或事件(相比同步Message来说),这里的全局顺序是指

    MessageQueue中的先后顺序,而且异步消息的处理不受MessageQueue中引入的enqueueSyncBarrier(long)方法的影响;

    具体见MessageQueue的next()方法中有段这样的代码:

        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());
        }

    也就是说当我们在队列中遇到一个sync barrier的时候,紧接着的同步Message的处理就会被本次循环忽略,而是直奔下一个

    异步的消息。sync barrier是通过MessageQueue中的enqueueSyncBarrier(long when)、removeSyncBarrier(int token)

    调用来实现添加、删除的;它们是在android.view.ViewRootImpl执行Traversals相关的代码时被调到的,代码如下:

        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
        final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); // 添加一个sync barrier
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                scheduleConsumeBatchedInput();
            }
        }
    
        void unscheduleTraversals() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); // 移除对应的sync barrier
                mChoreographer.removeCallbacks(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            }
        }
    
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); // 移除对应的sync barrier
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
                try {
                    performTraversals();
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }

      Message类的分析就到这了,以后会陆续分析下常见于Android开发中的类。。。(由于本人水平有限,欢迎批评指正)

  • 相关阅读:
    【转】总体样本方差的无偏估计样本方差为什么除以n-1
    【转】用PyQt5写的第一个程序
    向量的协方差计算
    数据挖掘模型
    数据挖掘方法论及实施步骤
    Hadoop MapReduce八大步骤以及Yarn工作原理详解
    传统数据仓库架构与Hadoop的区别
    数据库优化方案整理
    数仓interview总结
    十四个值得推荐的个人提升方法
  • 原文地址:https://www.cnblogs.com/xiaoweiz/p/3660245.html
Copyright © 2020-2023  润新知