Android Handler机制3--Message简介

移步Android Handler机制详解

  • 定义一个可以发送给Handler的描述和任意数据对象的消息。
  • 此对象包含两个额外的int字段和一个额外的对象字段,这样就可以使用在很多情况下不用做分配工作。
  • 尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(),这样是从一个可回收的对象池中获取Message对象。

1 Message成员变量解析

1.1 成员变量 what

  • Message用一个标志来区分不同消息的身份,不同的Handler使用相同的消息不会弄混,一般使用16进制形式来表示

代码在Message.java 39行

用户定义的Message的标识符用以分辨消息的内容。Hander拥有自己的消息代码的命名空间,因此你不用担心与其他的Handler冲突。

1.2 成员变量 arg1和arg2

  • arg1和arg2都是Message类的可选变量,可以用来存放两个整数值,不用访问obj对象就能读取的变量。

代码在Message.java 46行

/**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1; 

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

如果你仅仅是保存几个整形的数值,相对于使用setData()方法,使用arg1和arg2是较低成本的替代方案。

1.3 成员变量 obj

/**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     * 
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;
  • 将一个独立的对象发送给接收者。当使用Messenger去送法消息,并且这个对象包含Parcelable类的时候,它必须是非空的。对于其他数据的传输,建议使用setData()方法
  • 请注意,在Android系统版本FROYO(2.2)之前不支持Parcelable对象。

1.4 其他成员变量

// 回复跨进程的Messenger 
    public Messenger replyTo;

    // Messager发送这的Uid
    public int sendingUid = -1;

    // 正在使用的标志值 表示当前Message 正处于使用状态,当Message处于消息队列中、处于消息池中或者Handler正在处理Message的时候,它就处于使用状态。
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    // 异步标志值 表示当前Message是异步的。
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    // 消息标志值 在调用copyFrom()方法时,该常量将会被设置,其值其实和FLAG_IN_USE一样
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    // 消息标志,上面三个常量 FLAG 用在这里
    /*package*/ int flags;

    // 用于存储发送消息的时间点,以毫秒为单位
    /*package*/ long when;

    // 用于存储比较复杂的数据
    /*package*/ Bundle data;
    
    // 用于存储发送当前Message的Handler对象,前面提到过Handler其实和Message相互持有引用的
    /*package*/ Handler target;
    
    // 用于存储将会执行的Runnable对象,前面提到过除了handlerMessage(Message msg)方法,你也可以使用Runnable执行操作,要注意的是这种方法并不会创建新的线程。
    /*package*/ Runnable callback;
 
    // 指向下一个Message,也就是线程池其实是一个链表结构
    /*package*/ Message next;

    // 该静态变量仅仅是为了给同步块提供一个锁而已
    private static final Object sPoolSync = new Object();

    //该静态的Message是整个线程池链表的头部,通过它才能够逐个取出对象池的Message
    private static Message sPool;

    // 该静态变量用于记录对象池中的Message的数量,也就是链表的长度
    private static int sPoolSize = 0;
  
    // 设置了对象池中的Message的最大数量,也就是链表的最大长度
    private static final int MAX_POOL_SIZE = 50;

     //该版本系统是否支持回收标志位
    private static boolean gCheckRecycle = true;

2 获取Message对象

2.1 Message构造函数

代码在Message.java 475行

    /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }

2.2 Message.obtain()方法

我们来看下Message.java 类的结构图

5713484-952c64fcc0bf394f.png

Message.居然有8个obtain函数
分为两大类

  • 无参的obtain()方法
  • 有参的obtain()方法

3 Message的消息对象池和无参的obtain()方法

3.1 无参的obtain()

代码在Message.java 122行

/**
     * 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) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                 // flags为移除使用标志
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
  • 从全局的pool返回一个实例化的Message对象。这样可以避免我们重新创建冗余的对象。

3.2 sPool

代码在Message.java 111行

   private static Message sPool;
  • 好像也没什么嘛?就是一个Message对象而已,所以sPool默认是null。
  • 除了public static Message obtain()里面的if (sPool != null) {}里面外,还有recycleUnchecked给sPool赋值了

3.3 recycleUnchecked()

代码在Message.java 291行

/**
     * 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) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

3.4 消息对象池的理解

把recycleUnchecked()和obtain()合在一起,省略一些不重要的代码

void recycleUnchecked() {
                ...
        if (sPoolSize < MAX_POOL_SIZE) {
                // 第一步
                next = sPool;
                 // 第二步
                sPool = this;
                 // 第三步
                sPoolSize++;
                 ...
         }
    }

public static Message obtain() {
    synchronized (sPoolSync) {
        //第一步
        if (sPool != null) {
            // 第二步
            Message m = sPool;
            // 第三步
            sPool = m.next;
            // 第四步
            m.next = null;
            // 第五步
            m.flags = 0; 
            // 第六步
            sPoolSize--;
            return m;
        }
    }
}

3.4.1 recycleUnchecked()的理解

假设消息对象池为空,从new message开始,到这个message被取出使用后,准备回收 先来看recycleUnchecked()方法

  • 第一步,next=sPool,因为消息对象池为空,所以此时sPool为null,同时next也为null。
  • 第二步,spool = this,将当前这个message作为消息对象池中下一个被复用的对象。
  • 第三步,sPoolSize++,默认为0,此时为1,将消息对象池的数量+1,这个数量依然是全系统共共享的。

这时候假设又调用了,这个方法,之前的原来的第一个Message对象我假设定位以为message1,依旧走到上面的循环。

  • 第一步,next=sPool,因为消息对象池为message1,所以此时sPool为message1,同时next也为message1。
  • 第二步,sPool = this,将当前这个message作为消息对象池中下一个被复用的对象。
  • 第三步,sPoolSize++,此时为1,将消息对象池的数量+1,sPoolSize为2,这个数量依然是全系统共共享的。

以此类推,直到sPoolSize=50(MAX_POOL_SIZE = 50)

3.4.2 obtain()的理解

假设上面已经回收了一个Message对象,又从这里获取一个message,看看会发生什么?

  • 第一步,判断sPool是否为空,如果消息对象池为空,则直接new Message并返回
  • 第二步,Message m = sPool,将消息对象池中的对象取出来,为m。
  • 第三步,sPool = m.next,将消息对象池中的下一个可以复用的Message对象(m.next)赋值为消息对象池中的当前对象。(如果消息对象池就之前就一个,则此时sPool=null)
  • 第四步,将m.next置为null,因为之前已经把这个对象取出来了,所以无所谓了。
  • 第五步,m.flags = 0,设置m的标记位,标记位正在被使用
  • 第六步,sPoolSize--,因为已经把m取出了,这时候要把消息对象池的容量减一。

将sPool看成一个指针,通过next来将对象组成一个链表,因为每次只需要从池子里拿出一个对象,所以不需要关心池子里具体有多少个对象,而是拿出当前这个sPool所指向的这个对象就可以了,sPool从思路上理解就是通过左右移动来完成复用和回收

4 obtain()有参函数解析

4.1 ② public static Message obtain(Message orig)

代码在Message.java 142行

/**
     * Same as {@link #obtain()}, but copies the values of an existing
     * message (including its target) into the new one.
     * @param orig Original message to copy.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }


4.2 public static Message obtain(Handler h)

代码在Message.java 164行

/**
     * 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;
    }

4.3 public static Message obtain(Handler h, Runnable callback)

代码在Message.java 178行

/**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

4.4 ⑤ public static Message obtain(Handler h, int what)

代码在Message.java 193行

/**
     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
     * <em>what</em> members on the Message.
     * @param h  Value to assign to the <em>target</em> member.
     * @param what  Value to assign to the <em>what</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

4.5 #### public static Message obtain(Handler h, int what, Object obj)

代码在Message.java 209行

/**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
     * members.
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param obj  The <em>object</em> method to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

4.6 public static Message obtain(Handler h, int what, int arg1, int arg2)

代码在Message.java 228行

/**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, 
     * <em>arg1</em>, and <em>arg2</em> members.
     * 
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

4.7 ⑧ public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

代码在Message.java 249行

/**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, 
     * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
     * 
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @param obj  The <em>obj</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, 
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

5 Message的 浅拷贝

Message的浅拷贝 就是copyFrom(Message o)函数
代码在Message.java 320行

/**
     * Make this message like o.  Performs a shallow copy of the data field.
     * Does not copy the linked list fields, nor the timestamp or
     * target/callback of the original message.
     */
    public void copyFrom(Message o) {
        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
        this.what = o.what;
        this.arg1 = o.arg1;
        this.arg2 = o.arg2;
        this.obj = o.obj;
        this.replyTo = o.replyTo;
        this.sendingUid = o.sendingUid;

        if (o.data != null) {
            this.data = (Bundle) o.data.clone();
        } else {
            this.data = null;
        }
    }
  • 其实从本质上看就是从一个消息体复制到另一个消息体。
  • 从代码中可以看出大部分数据确实都是浅拷贝,但是对于data这个Bundle类型的成员变量却进行了深拷贝,所以说该方法是一个浅拷贝方法感觉也不是很贴切。

参考

Android Handler机制5之Message简介与消息对象对象池

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

推荐阅读更多精彩内容