在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


用图形表示如下:

wKiom1OjD_XCsf_oAABhNZKd0zo067.jpg

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

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. 第一行代码笔记之——Activity启动模式
  2. Android:简单联网获取网页代码
  3. Android 开发笔记 - Android Studio 代码模板
  4. android源代码下载及编译
  5. android 用代码画圆
  6. android用代码实现圆角背景
  7. 小编程(三):用户登录注册界面开发及用户信息管理案例代码详解
  8. Android沉浸式状态栏下,如何代码实现android:fitsSystemWindows="

随机推荐

  1. Android working with Volley Library
  2. 通过设置android:imeOptions来改变软键盘
  3. Android 5.0.1 Layout_toLeftOf不生效
  4. Android xml资源文件animal动画解析
  5. opencv for android 编译
  6. Android 电源管理
  7. Android Bitmap
  8. Android 自定义ViewPager 实现轮播图
  9. Android Tablayout 的使用
  10. Android使用默认浏览器打开网页