Handler消息机制之深入理解Message.obtain()

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chenbaige/article/details/79473475

前言:在我们日常开发工作中,难免经常会在线程间进行消息传递,而这个过程最常用的实现方式就是Handler消息机制。当然,这并不我们今天的重点,今天我们要重点关注的是消息传递的中间体:Message。我们怎么去获取Message对象?可能大多数同学直接通过 new Message() 的方式创建一个新的对象;对性能和效率有了解的同学可能就会通过 handler.obtainMessage()Message.obtain() 的方式去得到这个Message对象。这几种方式有什么不同?效率和性能有什么差异?看到这里,你还是一脸懵b??是的话,就跟着我的思路继续看下去;如果你还有一副胸有成竹的感觉,我只能说:大佬,小弟给你递茶!!话不多说,切入正题。

  • Message创建方式一:Message message = new Message()
    这种方法很常见,就是常见的创建对象的方式。每次需要Message对象的时候都创建一个新的对象,每次都要去堆内存开辟对象存储空间,对象使用完后,jvm又要去对这个废弃的对象进行垃圾回收。看到这里,你可能会说:我们平时使用对象不都是这样的嘛,有什么大惊小怪的?难道我们平时操作对象的方式都存在效率和性能的问题?

    面对大家的各种质疑,我只想说:先喝杯咖啡冷静一下,听我给你娓娓道来!!俗话说:没有对比,就没有伤害。我们平时使用的方式并没有什么问题,只是这里有效率更高的获取方式。

  • Message创建方式二:Message message = handler.obtainMessage()
    上面说到,这种方式去获取Message,效率会更高!但是,何以见得??别急,下面我们从源码的角度为大家一一解析。先看handler.obtainMessage()的源码:

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

因为obtainMessage方法是Handler类的方法,因此这里的this指代的是调用obtainMessage方法的那个Handler:即handler.obtainMessage()中的handler对象。对this还有不清楚的,请移步 this用法详解 查看详细讲解。接着看obtain方法源码:

 public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

从源码可以看到,这里是通过Message类的内部方法obtain()去获取这个Message对象,这就和我们接下来要讲解的第三种方式Message.obtain()调用逻辑一样了,我们接下来详细分析。插入一句题外话:Message类中的target是一个Handler对象,主要用于后期消息的处理。欲知详情,请移步 Handler消息机制。接下来我们详解:Message.obtain()

  • Message创建方式三:Message message = Message.obtain()
    打开Message类的obtain方法,源码如下:
  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();
    }

看到这里,可能我们还只是一知半解。因为方法里面有几个关键变量在迷惑我们,先弄清它们的含义,才能帮助我们更好的理解,接下来看一下这几个变量的定义:

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

没错,看到这里,理解起来就容易多了:

  1. sPoolSync :主要是给Message加一个对象锁,不允许多个线程同时访问Message类和obtain方法,保证获取到的sPool是最新的。
  2. sPool:存储我们循环利用Message的单链表。在Handler消息机制中说过Message的数据结构,因此这里sPool只是链表的头节点。
  3. sPoolSize:单链表的链表的长度,即存储的Message对象的个数。

理解了这几个变量的涵义,对obtain方法的理解就更轻松了。对数据结构有了解的同学,一看obtain方法的实现逻辑就知道,它就是对链表的操作。具体逻辑如下:

  1. synchronized (sPoolSync):给对象加锁,保证同一时刻只有一个线程使用Message。
  2. if (sPool != null):判断sPool链表是否是空链表,如果是空,就直接创建一个Message对象返回;否则就进入第三步。
  3. 链表操作:将链表头节点移除作为重用的Message对象,第二个节点作为新链表(sPool )的头节点。
 Message m = sPool;
 sPool = m.next;
 m.next = null;
具体操作如下图:

这里写图片描述
4.最后一步:链表的长度减一,把第三步得到的Message返回,用于重复利用,执行代码如下:

  sPoolSize--;
  return m;

总结:这就是通过obtain方法获取Message对象的详情。通过obtain方法获取Message对象使得Message到了重复的利用,减少了每次获取Message时去申请空间的时间。同时,这样也不会永无止境的去创建新对象,减小了Jvm垃圾回收的压力,提高了效率。

猜你喜欢

转载自blog.csdn.net/chenbaige/article/details/79473475
今日推荐