【构建Android缓存模块】(一)吐槽与原理分析

声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)  java

http://my.oschina.net/ryanhoo/blog/93285 程序员

    摘要在我翻译的Google官方系列教程中,Bitmap系列由浅入深地介绍了如何正确的解码Bitmap,异步线程操做以及使用Fragments重用等技术,而且在最后给出了很是强大的独家秘笈:BitmapFun,让猿媛们得以一窥究竟Google的攻城师们是如何高屋建瓴地秒杀OOM的。 缓存

    前言 异步

    在下载到BitmapFun.rar这个神圣的压缩包之后,我是双手颤抖,彷佛是打开上古秘藏通常,心情激动致使久久不能自已。我还记得那天上海下着小雨,我当时霍然起身,伫立在23楼的窗台,仰着头向江水对岸的东方明珠望去,彷佛这样我郁积已久的眼泪就不能掉下来。说到这里,Ryan又暗自抹了一把眼泪。短暂地忘记了过去的黑暗时光,那一个漫长的被OOM的淫威所折磨的盛夏。。。 学习

    最后在Boss诧异的目光中,我回到办公桌,按捺着心里汹涌的情绪波动,而后当心翼翼的打开BitmapFun.rar。当那些在洪荒时代就活跃在Android平台的大师们书写的篇章呈如今我眼前时,个人表情与阿宝从师父手里获得Dragon Scroll时通常,永久的定格在了极度天真的期待与眼角一抽一抽的状态。 测试

    那些泛黄的代码在我看去,通篇只有一句话:老子看不懂! spa

自力更生,构建本身的缓存模块 .net

    Google的这个demo堪称详尽,考虑极其周详,天然是极好的。可是当原理被层层的“特殊状况”包装起来,本来简单的例子变得异常复杂,几个类之间的关系错综复杂,堪比吸血鬼日记几个帅哥美女之间的关系。要理解清楚每一句代码的含义,你必定要有理解Matt那人老珠黄的老娘和他和失落的好朋友Taylor搞在一块儿的觉悟。 线程

    好了,吐槽一下就收,千万不要怀疑Google,人家已经仁至义尽了。BitmapFun中在下载后将Bitmap缓存起来,缓存作了两份:LruCacheDiskLruCache,分别是内存缓存硬盘缓存。此外两个相当重要的类是: 翻译

BitmapWorkerTask(ImageView imageView)
    
AsyncDrawable extends BitmapDrawable
    AsyncDrawable(Resources res, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask)

    BitmapWorkerTask持有一个WeakReference<ImageView> imageViewReference,弱引用ImageView,用做异步处理加载图片的任务。

    AsyncDrawable巧妙的引用持有弱引用WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference,是BitmapDrawable的子类,这样就能够setImageBitmap(AsyncDrawable)

    关系:AsyncDrawable中弱引用BitmapWorkerTask。实际上是图片引用ImageView的关系,而ImageView.getDrawable又能够得到图片。这种高妙的思想不是正值得咱们学习么?   

    固然,这节课并非讲解官方Demo的,在讲解它以前,咱们先来学习一个更加简单的缓存实现方案,使用最简单的方式快速构建本身应用的缓存模块,有效避免OOM异常。它的难度很是小也很方便理解,能够在这个缓存实现的基础上,咱们再去理解更加高妙的BitmapFun的缓存实现方案。

    后面将要介绍的缓存方案已经应用在一个项目中(该项目将于13年1月20开源,使用Github托管,记念我22岁的生日),效果至关不错,下载并显示上百张Bitmap也异常流畅,甚至没有半点的停顿,全程使用Emulator测试也没有出现过OOM异常,内存处于可控状态。

如何解决OOM

    Bitmap之因此容易引发OOM异常,缘由已经在Bitmap系列教程中说的明明白白。可是咱们至少清楚一点:一个手机屏幕再大,合理尺寸的Bitmap也不至于耗空全部内存,那要怎么作才能避免OOM呢?

  • 加载合理尺寸的Bitmap
  • 避免反复解码、重复加载Bitmap
  • 控制Bitmap的生命周期,合理回收

     此外网上也有很多歪门邪道,我我的认为是不可取的,使用这些简单粗暴的方法,后期会为你带来更大的麻烦

  • 减损图片质量(使用太高的inSampleSize值)
  • 使用decodeStream(绕过Java层,直接调用JNI)
  • 强制增长heap size
  • 其余

    控制Bitmap的生命周期才是正解,BitmapFun使用的LruCache是它将最近被引用到的对象存储在一个强引用的LinkedHashMap中,而且在缓存超过了指定大小以后将最近不常使用的对象释放掉

    Memory Cache的Size是受限的,所以加入DiskLruCache,虽然在访问速度上逊于Memory Cache,可是速度也是至关可观的。

    借鉴Google的作法,我也将缓存作了两份,一份是Memory Cache,使用弱引用的WeakHashMap来控制Bitmap的生命周期,后面会有详细解释。另外一份严格来讲不能算是缓存,直接将文件存储在SDCard上,避免重复下载。

佛说引用,既非引用,是名引用。

    关于引用,或许对于小菜鸟们不是很好理解(我碰到过太多Java都没学好来作Android的,基础很重要!。我使用金刚经的著名三段论来解释它:佛说XX,既非XX,是名XX

    这句话什么意思呢?好比佛说大米,能够说它不是大米,只是名字叫作大米罢了。不会由于你为它更名叫作大麦而改变它的本质,你叫它作水,吃到嘴里的仍是原来的味道

    关于引用,跟这个有着很是类似的共性。引用就至关于实际对象的名字,好比下面的例子:

Person p1 = new Person();
Person p2 = null;
p2 = p1;
p1 = null;

    new Person()这个对象的名是p1,然后你将名字改为了p2,对象仍是那个对象,不会由于你将p1的大名盖在null的头上而改变它的本质。以上的p1和p2都是引用,它们都不过是名。

    在了解到引用的含义后,虚拟机会告诉你,被引用的对象处于可得到(reachable)状态,它是你的好管家,既然你要用它,它就不会回收它。想一想若是你正在吃一只烤鸭,人家忽然一把抢了过去扔垃圾桶了你什么感受。

    若是在上面的那段程序后面加上p2 = null,Person这个对象就没有任何引用指向它了,垃圾回收器会在不肯定的时间进行回收你都把东西扔了,总不能不让人家收破烂吧?)

    若是你想继续持有这个对象的引用,但愿能够继续访问,可是也容许垃圾回收器进行回收,该怎么办呢(你想减肥,告诉你的好朋友说,若是察觉到你太胖了,就将你嘴里的烤鸭抢去扔了。如果你很饿,身材也不错,你要继续吃。

    这个时候,咱们须要借助Java提供的软/弱/虚引用。咱们平时使用的如p1和p2这样的叫作强引用(Strong Reference)。要使垃圾回收器能在内存不够的时候,主动抢下你嘴里的烤鸭,进行回收,须要使用这些:

  • 引用:SoftReference
  • 弱引用:WeakReference
  • 虚引用:PhantomReference

    它们按照由强到弱的引用关系排列,虚引用至关于几乎没有引用。文艺青年常说的若即若离用来形容它再恰当不过了

    关于这三个引用的具体学习,详见我提供的参考资料。这里只是向你解释为何使用弱引用能够起到防止Bitmap过多而致使内存紧张的做用

    在这里,因为我须要使用Bitmap和名字的key-value对应关系,我使用Java提供的WeakHashMap(String key, Bitmap value),顾名思义,它用来保存WeakReference,而且确保每一个key只对应一个值,在内存不够的时候,垃圾回收器会进行回收。当key值索引不到Bitmap,再进行其余的操做。

原理示意图

    我将原理画成图,以便你们的理解。主体有三个,分别是UI,缓存模块和数据源。它们之间的关系以下:

    UI:请求数据,使用惟一的Key值索引Memory Cache中的Bitmap。

     内存缓存:缓存搜索,若是能找到Key值对应的Bitmap,则返回数据。不然执行第三步。

     硬盘存储:使用惟一Key值对应的文件名,检索SDCard上的文件。

     若是有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。若是没有对应文件,执行第五步。

     下载图片:启动异步线程,从数据源下载数据(Web)。

    ⑥ 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。

:这节课除了吐槽,主要的仍是原理分析。若是你有更好的缓存方案,欢迎提出。下节课将讲解具体的Memory Cache和FileCache如何实现。

参考资料:

【1】Thinking In Java 4th Chapter 17.12 Hoding References.pdf  http://vdisk.weibo.com/s/jtqjr

【2】李刚:突破程序员基本功的16课之Java的内存回收.pdf  http://vdisk.weibo.com/s/jtqik

相关文章
相关标签/搜索