在用Handler时,我们使用Message对象时,google不推荐我们通过new Message()来构造一个Message对象,因为Message我们会频繁的使用,不断的new,导致内存碎片,效率不高。Google推荐我们通过obtain()方法构造,为什么了?obtain()内部是怎么实现构造Message从而避免频繁的new而造成的浪费?
首先我们看看obtain()函数:
1 public static Message obtain() { 2 synchronized (sPoolSync) { 3 if (sPool != null) { 4 Message m = sPool; 5 sPool = m.next; 6 m.next = null; 7 sPoolSize--; 8 return m; 9 } 10 } 11 return new Message(); 12 }
其他重载的obtain()函数里面都是调用了obtain(),所以只需要搞清楚这一个就成了。
首先判断sPool != null,这儿sPool是Message的一个静态内部对象:
private static Message sPool;
如果我们首次调用sPool肯定是null了,那么该函数也就直接执行 return new Message()了。执行完sPool仍然是null,那么sPool在初始化了,我们看到其在recycle()中初始化。
1 public void recycle() { 2 clearForRecycle(); 3 4 synchronized (sPoolSync) { 5 if (sPoolSize < MAX_POOL_SIZE) { 6 next = sPool; 7 sPool = this; 8 sPoolSize++; 9 } 10 } 11 }
该函数被调用的地方很多,但主要是在Looper.loop()中,这儿实现了回收,开始时sPoolSize小于MAX_POOL_SIZE(该值为10),那么每调用一次recycle(),sPool,next就会被赋值,sPoolSize++,直到sPoolSize = MAX_POOL_SIZE。在这儿sPoolSize++的过程中,sPoll指向调用者对象,保留该对象的引用,next指向前一个对象的引用,注意,这儿next是Message的内部属性成员,不是static的,所以这样下来就形成了一个单向链表,每个节点是调用了recycle()方法的Message对象,该链表的最大长度是MAX_POOL_SIZE。
我们回过头来再看obtain(),当sPool != null时,m = sPool,其作为返回值,然后给sPool重新赋值:sPool = m.next,然后m.next = null,sPoolSize--。这个操作其实就是删除链表的最后节点并返回的操作。由此可见原先new的Message对象在这儿都给返回重新使用,实现了重复利用。
Handler中的消息队列其实也就是一个Message对象,也就是说消息对写并不是MessageQueue来维护,而是由Message自己来实现的。