2019-08-10-Handler机制之Message介绍

Handler机制之Message介绍

提到handler消息机制,必然少不了要介绍Handler中最重要的消息载体Message

1,Message可以携带的信息

  • 以下是直接对外暴露的属性

      /**
       * 用户定义的消息代码,以便当接受到消息是关于什么的。其中每个Hanler都有自己的命名空间,不用担心会冲突
       */ 
       public int what;
      /**
       * 如果你只想存很少的整形数据,那么可以考虑使用arg1与arg2,
       * 如果需要传输很多数据可以使用Message中的setData(Bundle bundle)
       */
       public int arg1;
      /**
       * 如果你只想存很少的整形数据,那么可以考虑使用arg1与arg2,
       * 如果需要传输很多数据可以使用Message中的setData(Bundle bundle)
       */
       public int arg2;
      /**
       * 发送给接受方的任意对象,在使用跨进程的时候要注意obj不能为null
       */
       public Object obj;
      /**
       * 在使用跨进程通信Messenger时,可以确定需要谁来接收
       */
       public Messenger replyTo;
      /**
       * 在使用跨进程通信Messenger时,可以确定需要发消息的uid
       */
       public int sendingUid = -1;
    
  • 以下是没有对外直接暴露的属性

      /**
       * 如果数据比较多,可以直接使用Bundle进行数据的传递
       */
       Bundle data;
      
      /*package*/ static final int FLAG_IN_USE = 1 << 0;
      
      /** If set message is asynchronous */
      /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
      
      /** Flags to clear in the copyFrom method */
      /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
      /**
       * 消息的标记
       * 1,是否使用
       * 2,是否是异步消息
       * 3,
       */
      /*package*/ int flags;
      /**
       * 消息正在需要被处理的事件,是从开机到需要被处理的时间
       * 并不是时间戳
       */
      /*package*/ long when;
      /**
       * 发送和要处理消息的Handler对象
       */
      /*package*/ Handler target;
      /**
       * Handler发送和要处理的Runable对象
       */
      /*package*/ Runnable callback;
      /**
       * 当前消息的下一个对象
       */
      // sometimes we store linked lists of these things
      /*package*/ Message next;
    

2,Message消息的实现方式

  • 直接new Message()来实现,这种方式虽然可以实现,但是效率不高,因为如果Handler需要发送和处理消息过多,频率较高的情况下,可能出现频繁的创建和销毁Message对象,严重的情况下可能造成内存问题

    基于此Android为我们提供了第二种实现方式

  • 实现Message的第二种方式就是使用Message.obtain()方法,内部是在handler的消息池中获取message实例,好处就是可以循环利用,不必另外开辟内存空间,效率比第一种方式直接创建实例要高

    public static final Object sPoolSync = new Object();
    private static Message sPool;       
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

从上方我们可以看出,

  • 消息池的内部是以链表的形式实现的
  • 获取消息的方式是以移除链表头sPool所指向的节点的形式

3,Handler消息池的实现原理

private static final Object sPoolSync = new Object();//控制获取从消息池中获取消息。保证线程安全
private static Message sPool;//消息池
private static int sPoolSize = 0;//消息池中回收的消息数量
private static final int MAX_POOL_SIZE = 50;//消息池最大容量

从Message的消息池设计,我们大概能看出以下几点:

  • 该消息池在同一个消息循环中是共享的(sPool声明为static),
  • 消息池中的最大容量为50,
  • 从消息池获取消息是线程安全的。

4,回收消息池

void recycleUnchecked() {
    //用于表示当前Message消息已经被使用过了
    //清除之前Message的数据
    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) {
        //判断当前消息池中的数量是不是小于最大数量,其中 MAX_POOL_SIZE=50
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            //增加线程池数量
            sPoolSize++;
        }
    }
}

5,Handler的消息回收机制

虽然Handler.remove消息的方式很多,但是最终都是使用了MessageQueue的移除消息方式

public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

接下来我们看下mQueue.removeCallbacksAndMessages的实现

void removeCallbacksAndMessages(Handler h, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;

        // Remove all messages at front.
        while (p != null && p.target == h
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

从上面我们可以看到,Handler是使用了俩次循环从消息链表中移除消息的

  • 第一次是将当前Handler发送的所有消息从消息链表中移除,注意::这里并不会将整个MessageQueue中的当前Handler发送的消息全部移除,而是在遍历过程中如果有其他的handler发送的消息则跳出循环,将mMessages指向当前头节点
  • 第二次是从第一次之后的头节点开始依次循环链表,找出当前Handler发送的消息,然后移除

至于为什么会要俩次循环移除消息呢?

  • 在Handler机制中,多个Handler对应一个Looper和MessageQueue,它们三者的比例是N:1:1,所以说MessageQueue中有多个不同Handler发送的Message

  • 并不能保证当移除消息的时候,对应的Handler就不继续发送消息了,也就是说该Handler发送的消息仍然会被添加到MessageQueue中,所以为了保证将整个MessageQueue中该Handler发送的消息全部被移除,在第一次循环移除之后,我们必须要再执行一次循环移除操作。

6,总结

  • 在使用Handler发消息时,建议使用Message.obtin()方法,从消息池中获取消息。
  • 在Message中消息池是使用链表的形式来存储消息的。
  • 在Message中消息池中最大允许存储50条的消息。
  • 在使用Handler移除某条消息的时候,该消息有可能会被消息池回收。(会判断消息池是否仍然能存储消息)

Message及Message回收机制

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