前言:在我们日常开发工作中,难免经常会在线程间进行消息传递,而这个过程最常用的实现方式就是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;
没错,看到这里,理解起来就容易多了:
- sPoolSync :主要是给Message加一个对象锁,不允许多个线程同时访问Message类和obtain方法,保证获取到的sPool是最新的。
- sPool:存储我们循环利用Message的单链表。在Handler消息机制中说过Message的数据结构,因此这里sPool只是链表的头节点。
- sPoolSize:单链表的链表的长度,即存储的Message对象的个数。
理解了这几个变量的涵义,对obtain方法的理解就更轻松了。对数据结构有了解的同学,一看obtain方法的实现逻辑就知道,它就是对链表的操作。具体逻辑如下:
- synchronized (sPoolSync):给对象加锁,保证同一时刻只有一个线程使用Message。
- if (sPool != null):判断sPool链表是否是空链表,如果是空,就直接创建一个Message对象返回;否则就进入第三步。
- 链表操作:将链表头节点移除作为重用的Message对象,第二个节点作为新链表(sPool )的头节点。
Message m = sPool;
sPool = m.next;
m.next = null;
具体操作如下图:
这里写图片描述
4.最后一步:链表的长度减一,把第三步得到的Message返回,用于重复利用,执行代码如下:
sPoolSize--;
return m;
总结:这就是通过obtain方法获取Message对象的详情。通过obtain方法获取Message对象使得Message到了重复的利用,减少了每次获取Message时去申请空间的时间。同时,这样也不会永无止境的去创建新对象,减小了Jvm垃圾回收的压力,提高了效率。
网友评论