JSON

情景学习Android中的LruCache

字号+ 作者:H5之家 来源:H5之家 2016-01-17 18:20 我要评论( )

LruCache类是基于LRU算法的内存缓存管理类,在v4包下有一个,Android3.1及以上在android.util包下也有一个,注意两个的移除策略是不一样的。一、简单使用publicc

LruCache类是基于LRU算法的内存缓存管理类,在v4包下有一个,Android3.1及以上在android.util包下也有一个,注意两个的移除策略是不一样的。

一、简单使用 public class BitmapMemoryCache { private static final int maxSize = (int) (Runtime.getRuntime().maxMemory() / 6); private static final LruCache<String, Bitmap> sBitmapCache = new android.support.v4.util.LruCache<String, Bitmap>(maxSize){ (String key, Bitmap value) { return sizeOfBitmap(value); } }; public Bitmap put(String key, Bitmap bitmap) { if(TextUtils.isEmpty(key) || bitmap == null) { return null; } return sBitmapCache.put(key, bitmap); } public Bitmap get(String key) { if(TextUtils.isEmpty(key)) { return null; } return sBitmapCache.get(key); } @SuppressLint("NewApi") (Bitmap bitmap) { if(bitmap == null) { return 0; } //API 19及以上版本 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return bitmap.getAllocationByteCount(); //API 12 ~ API 18 } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { return bitmap.getByteCount(); //API 12以下 } else { return bitmap.getRowBytes() * bitmap.getHeight(); } } }

A:上述代码有问题吗?有的话有哪些?

B:sBitmapCache是类变量而不是成员变量,不管我new了多少个BitmapMemoryCache对象然后进行put操作,其实put的都是同一个sBitmapCache,这与我们直觉是相违背的。

A:怎么改进?

B:可以考虑把sBitmapCache声明称非static的。

A:那假如现在我new了100个BitmapMemoryCache对象,也就是有100个sBitmapCache缓存池,按照代码中一个sBitmapCache的缓存池大小是虚拟机最大堆大小的1/6,显然100*1/6大大超过了虚拟机的最大堆大小,这样很容易导致OOM吧?

B:哦,那还是把sBitmapCache声明成static吧,所有缓存统一管理,因为虚拟机堆是共用,然后把BitmapMemoryCache做成单例就行。

A:这样做可以,不过把BitmapMemoryCache做成单例后有一个问题,那个单例对象一直持有sBitmapCache引用,而sBitmapCache持有许多Bitmap的引用,因此在不用的时候要及时解除引用好让垃圾回收器回收掉,否则会使一部分堆内存一直被占用。

A:上面的代码还有其它问题吗?有没有可能会发生OOM?

B:已经指定缓存池大小为最大虚拟机堆大小的1/6了,因此缓存池最多就占用1/6的最大虚拟机堆,应该不会发生OOM吧。

A:那如果其它地方占用的堆大小超过5/6呢?

B:对哦,sBitmapCache最多允许放1/6的数据,而实际上如果可用堆已经少于最大堆的1/6了,这时再put就有可能发生OOM。

A:那需要怎么改进?

B:在put方法里,每次要将缓存添加到sBitmapCache时,先判断一下可用堆大小还有多少,如果已经小于一个临界值那么我就不缓存该Bitmap,或者按照一定的规则(比如LRU算法)把缓存池里面的某些Bitmap移除掉再添加当前Bitmap进去。

二、深入理解LruCache实现原理

A:LruCache是怎么实现LRU算法的?

B:其内部使用的是一个LinkedHashMap保存缓存数据,而LinkedHashMap其实已经实现了LRU算法,因此LruCache其实是使用了LinkedHashMap的实现。

A:能否具体说一下LinkedHashMap的LRU算法是怎么实现的。

B:LinkedHashMap默认情况下并没有开启LRU功能,因为是使用双向循环链表实现,因此其迭代顺序就是添加顺序,但我们可以在其构造函数指定其开启LRU功能,具体到代码就是指定accessOrder为true,然后每次访问该LinkedHashMap时,它会把该节点移到链表的尾部,这样越靠近链头就是越少访问的数据了,以后超出最大容量了直接从链头开始删起就行。

A:你说的是v4包下的LruCache吧,其实在Android3.1开始,android.util包下也有一个LruCache,它们是完全一样的吗?有没有什么区别?

B:v4包下的LruCache才是真是按LRU算法移除掉元素的,也就是从LinkedHashMap的链头开始移除,而android.util包下的是从LinkedHashMap的链尾开始移除的,也就是移除最经常使用的元素,这个区别体现在它们的trimToSize方法的实现不同,而它们的其它所有代码及逻辑都是相同的。

A:LruCache是线程安全的吗?

B:是,在put和get操作LruCache都进行了加锁操作(使用this作为锁对象),因此是线程安全的。

A:你觉得加锁方式有改进的地方?

B:get操作并不会修改数据,可以使用Java中ReadWriteLock读写锁实现锁机制,这样效率会高一点。

A:LruCache里面有一个叫create方法,它是做什么用的?

B:在get操作发现没有要获取的元素时,会调用该方法,子类可以重写该方法实现在获取不到元素时自行创建相应的元素,这时get返回的可能就是这个元素。create该方法默认实现返回null。

A:为什么说可能返回create方法返回的那个元素呢,为什么不是一定?

B:因为在调用create并对存放数据的map没有加锁,这时可能会有另外一个线程put一个相同key的元素进map,这时可以选择直接返回map中的值,也可以选择返回create返回的值,当然LruCache采用第一种方法实现。

A:最后在问一个,知道putCount,createCount,evictionCount,hitCount和missCount的含义吗?

B:putCount表示进行put的操作次数,createCount表示调用create的次数,evictionCount表示被移除的元素数,hitCount表示目中次数,missCount表示未命中次数。

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  •  JSON入门级学习总结-JSON数据结构

    JSON入门级学习总结-JSON数据结构

    2016-02-25 11:05

  • Android解析Json速度最快的库:json

    Android解析Json速度最快的库:json

    2016-02-13 18:00

  • ASP.NET Web API 2 返回 Json格式

    ASP.NET Web API 2 返回 Json格式

    2016-02-10 17:18

  • Android中JSON数据格式读取解析创建视频教程

    Android中JSON数据格式读取解析创建视频教程

    2016-02-05 19:00

网友点评
: