美文网首页Android开发Android开发经验谈Android技术知识
回转寿司你一定吃过!——Android消息机制(构造)

回转寿司你一定吃过!——Android消息机制(构造)

作者: 唐子玄 | 来源:发表于2019-02-01 10:09 被阅读9次

    消息机制的故事


    • 寿司陈放在寿司碟上,寿司碟按先后顺序被排成队列送上传送带传送带被启动后,寿司挨个呈现到你面前,你可以选择吃或者不吃。

    将Android概念带入后,就变成了Android消息机制的故事:
    寿司碟 ---> 消息(Message)
    队列 ---> 消息队列(MessageQueue)
    传送带 ---> 消息泵 (Looper)
    寿司 ---> 你关心的数据

    暂未找到 Handler 在此场景中对应的实体。它是一个更抽象的概念,它即可以生产寿司,又把寿司送上传送带,还定义了怎么享用寿司。暂且称它为消息处理器吧。

    如果打算自己开一家回转寿司店,下面的问题很关键:

    1. 如何生产寿司(如何构造消息)
    2. 如何分发寿司(如何分发消息)

    让我们带着这两个问题,去分析一下消息机制源码。
    (ps: 下文中的 粗斜体字 表示引导源码阅读的内心戏)

    如何构造消息


    寿司碟是重复利用的,享用完寿司后,它被清洗,然后被存放起来,以便再次利用。没有哪个老板会在用餐后把寿司碟销毁,下次要用就买新的,这样代价太大。
    同样道理,构造消息对象代价也很大,它是否也像寿司碟一样可以复用?如果是,那消息存放在哪里?
    让我们以Handler.obtainMessage()为切入点一探究竟:

     public final Message obtainMessage(){
         return Message.obtain(this);
     }
    

    它调用了Message.obtain(),源码如下:

    public final class Message implements Parcelable {
        //省略了非关键代码
        ...
        // sometimes we store linked lists of these things
        //指向消息链上下一个消息的引用
        /*package*/ Message next;
    
        //消息链头部引用,它是静态的,可以被所有消息对象共享
        private static Message sPool;
        //消息链长度
        private static int sPoolSize = 0;
    
        ...
        /**
         * Return a new Message instance from the global pool. Allows us to
         * avoid allocating new objects in many cases.
         */
        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    //1. 定义指向消息链头部引用
                    Message m = sPool;
                    //2. 定义新的消息链头部
                    sPool = m.next;
                    //3. 断链
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    //返回消息链头部消息
                    return m;
                }
            }
            //如果消息链为空则新建消息
            return new Message();
        }
        ...
    }
    
    • 如果对数据结构中的链表还有映像,obtain()就是在取链表头。图示如下:
      1
    2 3
    • 消息池是用链表结构实现的。那 Message一定有一个指向后续结点的“指针”,果不其然,在其成员变量中找到Message next;
    • 消息池头指针sPool是一个Message类型的静态变量,这表示所有Message都共享这一个消息池。
    • obtain()是从消息池中拿消息,那一定还有一个方法是往池里填消息,在Message类中搜索sPool使用的地方,找到如下这个方法:
        /**
         * Recycles a Message that may be in-use.
         * Used internally by the MessageQueue and Looper when disposing of queued Messages.
         */
        void recycleUnchecked() {
            // Mark the message as in use while it remains in the recycled object pool.
            // Clear out all other details.
            //清理消息携带的数据
            flags = FLAG_IN_USE;
            what = 0;
            arg1 = 0;
            arg2 = 0;
            obj = null;
            replyTo = null;
            sendingUid = -1;
            when = 0;
            target = null;
            callback = null;
            data = null;
    
            synchronized (sPoolSync) {
                //限制消息池大小
                if (sPoolSize < MAX_POOL_SIZE) {
                    //1. 回收的消息接入消息链
                    next = sPool;
                    //2. 回收的消息成为消息链新头部
                    sPool = this;
                    sPoolSize++;
                }
            }
        }
    
    • 正如猜想的那样recycleUnchecked()会将当前消息插入到消息链头部。图示如下
      1
    2
    • 读到这里,我们知道“消息从池中来最终又回到池中去”,那到底消息是在什么时候才会被回收到消息池呢? 好问题!这个问题要等分析完消息分发才能解答。但现在我们可以大胆的猜测一下:承载寿司的碟子会在寿司被享用完之后被厨房回收,那消息是不是再被处理完之后就被回收了?

    总结


    Android消息机制中的“构造消息”部分讲完了,总结一下:消息的生命周期会经历“创建-回收-再利用”,所有消息共享一个链表结构的消息池,它用于存放被回收的消息。

    故事还没有结束,下一篇会继续讲解“分发消息”。

    相关文章

      网友评论

        本文标题:回转寿司你一定吃过!——Android消息机制(构造)

        本文链接:https://www.haomeiwen.com/subject/ipsfsqtx.html