【構建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

相關文章
相關標籤/搜索