在Android的android.os.Message类的文档中有这么一句话:


While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.


大意是说,虽然Message类的构造方法是public的,你可以直接通过new来创建一个新的对象,但是最好还是通过Message.obtain()或者Handler.obtainMessage()来创建一个新的Message对象,因为这两个方法会重用之前创建的但是已经不再使用了的对象实例。


这句话不禁引起了我的兴趣,因为之前我写过一篇博客《Pool, SimplePool与SynchronizedPool》,在这篇博客里面仔细分析了Android是如何实现一个对象池的。里面提到VelocityTracker就是用SynchronizedPool来实现对象重用的,代码如下:

VelocityTrackertracker=VelocityTracker.obtain();tracker.recycle();

Message类看起来也略有相似,不过经过阅读Message类的源代码,发现我错了,Message类使用了另一种巧妙的方法来实现对象重用。


好了,不卖关子了,Message类使用了一个链表来实现对象池,而且是一个前端链表,即在前端插入和删除的链表,避免了插入和删除的时候遍历整个链表。是不是有点出人意料?


首先看一下这段代码,去除了Message中其他的携带消息信息的字段。已经很明显可以看出来是一个链表了吧。

publicfinalclassMessageimplementsParcelable{//省略其他代码Messagenext;//(1)privatestaticfinalObjectsPoolSync=newObject();//(2)privatestaticMessagesPool;//(3)privatestaticintsPoolSize=0;//(4)privatestaticfinalintMAX_POOL_SIZE=50;//省略其他代码}

(1) 声明了next指针

(2) 对象锁,Message对象池是线程安全的,这样就需要在向对象池申请和归还对象时使用锁

(3) 这里是关键,一个静态的Message对象,这就是这个链表的头指针了,因为是类变量,因此,整个JVM中只有一个

(4) 当前对象池的大小,后面还限制了这个Message对象池中的对象个数最大为50


用图形表示如下:

继续阅读代码,又发现了一个让人困惑的问题,看这个方法:

publicstaticMessageobtain(){synchronized(sPoolSync){//(1)if(sPool!=null){Messagem=sPool;//(2)sPool=m.next;//(3)m.next=null;//(4)sPoolSize--;//(5)returnm;}}returnnewMessage();}

这也很容易理解:

(1) 获取锁,这里毫无疑义

(2) 当头指针指向的对象不为null时,将这个对象赋值给m

(3) 将头指针指向m的next指针

(4) 将m的next指向null,到这里位置,我们从以sPool为头指针的链表中取出了第一个元素

(5) 将链表size减1


看起来没错,疑问是,当第一次获取对象的时候sPool肯定为null,那么这个if语句肯定不会执行,会直接执行最后一句return new Message(),直接创建一个对象?这样不是毫无意义了么?


看到这里,似乎感觉发现了一个Android的bug,会有这么明显的bug么?当然不会,静下心来继续读代码,读到这里的时候豁然开朗了:

publicvoidrecycle(){clearForRecycle();//(1)synchronized(sPoolSync){if(sPoolSize<MAX_POOL_SIZE){//(2)next=sPool;//(3)sPool=this;//(4)sPoolSize++;}}}

(1) 重置Message中携带的消息

(2) 检查当前对象池的大小是不是已经超过了最大值,如果当前队列已经满了,就不管这个对象了,让JVM的GC回收好了,这里保证了性能的同时兼顾了内存消耗

(3) 将当前对象next指针指向头指针sPool指向的对象

(4) 将头指针sPool指向当前对象,然后将对象池大小加1


到这里就明白了:这篇博客开头的那句话其实背后还有一些潜台词,那就是你必须显式的调用一下recycle()将当前的Message对象归还到对象池,这个对象池才能发挥其效果,不调用recycle()方法,对象不会归还,会被JVM GC回收。


也就是说下面两句话必须是成对出现的,不用obtain()而调用recycle()会导致不停的创建Message对象直到超过MAX_POOL_SIZE的限制而被对象池扔掉;通过obtain()申请对象而不用recycle()归还会导致对象池被消耗干净而不停申请新对象。

Messagemsg=Message.obtain();msg.recycle();

所以文档再完整还是不如看代码。


对于通过SynchronizedPool来实现对象池和这种通过链表来实现对象池两种方法,我看不出来各自有何优缺点,这两种方法很相似,实现的功能也相似,唯一的不同在于,前者似乎更容易扩展。也许你自己在其他项目中需要对象池的时候,可以借鉴一下这两种方法。

更多相关文章

  1. Android中的Parcelable接口
  2. ch029 Android(安卓)service aidl
  3. Android属性动画
  4. Android(安卓)OkHttp的Cookie自动化管理
  5. 图解 Android(安卓)Handler 线程消息机制
  6. 图解 Android(安卓)Handler 线程消息机制
  7. Android调用WebService系列之KSoap2对象解析
  8. Android调用WebService系列之KSoap2对象解析
  9. Android的内存泄漏和调试

随机推荐

  1. 解析:清除SQL被注入恶意病毒代码的语句
  2. 解析SQLServer任意列之间的聚合
  3. 浅析SQLServer中的Scanf与Printf
  4. 浅析被遗忘的SQLServer比较运算符修饰词
  5. 解析SQLServer获取Excel中所有Sheet的方
  6. 解析SQLServer2005的Top功能
  7. 解析关于SQL语句Count的一点细节
  8. 解析sql中得到刚刚插入的数据的id
  9. 使用SQL Server 获取插入记录后的ID(自动
  10. 如何区分SQL数据库中的主键与外键