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

这是“Android消息机制”系列的第一篇文章,系列文章目录如下:

  1. 回转寿司你一定吃过!——Android消息机制(构造)
  2. 回转寿司你一定吃过!——Android消息机制(分发)
  3. 回转寿司你一定吃过!——Android消息机制(处理)

消息机制的故事


寿司陈放在寿司碟上,寿司碟按先后顺序被排成队列送上传送带传送带被启动后,寿司挨个呈现到你面前,你有三种享用寿司的方法。

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

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

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

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

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

如何构造消息


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

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

public final class Message implements Parcelable {
  ...
  /**
    * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
    * @param h  Handler to assign to the returned Message object's <em>target</em> member.
    * @return A Message object from the global pool.
    */
  public static Message obtain(Handler h) {
     Message m = obtain();
     m.target = h;
     return m;
  }
  ...
}
  • 其中Message.target是一个Handler类型的成员变量。为啥Message要记录构造它的Handler对象? 好问题!但这个问题的解答需要等到分析消息处理的时候才能解答,先留个悬念。
  • 构造消息调用链的终点是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消息机制中的“构造消息”部分讲完了,总结一下:消息的生命周期会经历“创建-回收-再利用”,所有消息共享一个链表结构的消息池,它用于存放被回收的消息。

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

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

推荐阅读更多精彩内容