2012年9月15日 星期六

[Android] Memory Cache

Reference
  • reference 的可以分為多種強度,我們最熟悉和最常使用的是 strong reference。
  • Four different degrees of reference strength: strong, soft, weak, and phantom.
  • 強度的差異在於 reference 和 garbage collector 間的互相影響關係。

Strong Reference
// buffer 就是 strong reference
StringBuffer buffer = new StringBuffer();
  • 當 reference 指向一個以上的物件,便是 strong reference。
  • 可以防止此 reference 被 GC。
  • 缺點:
    • Memory: 因為使得物件不能被回收,所以有造成 memory leak 的危險。
    • Caching: 假設在 application 中應用到許多大圖片,為了一直 reload 而 cache 圖片,所以 always 會有 reference 指向圖片,使得它在 memory 且不會被回收,也就是說我們得決定它是否該被移除且使其可以回收。

WeakReference
weak reference 則是隨時有可能已被回收。
// API 使用方式如下,建立 Widget 的 WeakReference。
WeakReference weakWidget = new WeakReference<widget>(widget);
// 取出 Widget object。
// 注意: 因為物件可以被回收,所以可能會得到 null。
weakWidget.get()
WeakHashMap
  • 作用類似 HashMap,只是 key 是使用 weak reference,若 key 變成 garbage 則它的 entry 便自動會被移除。
  • 但並不是你啥也也沒做他就能自動釋放,而是你使用到 value 才會被釋放。
  • super(key, queue); 只有 key 才是 weak reference,value 仍是 strong reference,所以 System.gc() 後,key 會被清除,value 則是才被視為 weak reference 然後到被調用到了才會被清除。

Reference queues
  • 當 WeakReference 回傳 null 時表示指向的物件已被回收,WeakReference object 也無用了,所以如 WeakHashMap 會去移除這類的 WeakReference object,避免 WeakHashMap 一直成長,其中卻有很多無用的 WeakReference 。
  • ReferenceQueue 可以幫助你管理這些 dead reference。
    • 如果你將 ReferenceQueue 傳入 weak reference's constructor,當有 reference 指向的物件被回收時,該 reference 會自動被 insert 到 ReferenceQueue 中,所以你可以定期清裡 ReferenceQueue 也就會清理了 dead reference。

Soft references
  • 類似於 weak reference,差異是比 weak reference 不易被回收。
  • 因為 soft reference 會盡量被保留在 memory 所以適合做 image cache。

Phantom References
  • Its get() method always returns null.
  • 用處:
    • 可以讓你決定何時將物件從 memory 移除。ex: manipulating large images,你可以先確定現在這一張已被移除,再載入下一張以避免 OOM。
    • 避免 finalize() method 可以 "resurrect" 建立了 strong reference 的 object。
      • override finalize() 的物件至少要被視為 GC 對象兩次以上才能被回收到,而不能及時的被回收,也因此會有多個待回收的 garbage 存在著等著被回收。

Android itself employs some caching as part of the resources infrastructure, so you are already getting the benefit of memory caching.

使用 bitmap 時,記得使用 recycle(),可以幫助釋放空間,所以若是再呼叫 getPixels() or setPixels(),可能會得到 exception,所以建議當不會再使用到 bitmap 時再使用。

LruCache
  • API level 12 (3.1)
  • Support Library back to API level 4 (1.6).
  • 管理與 cache 物件,呼叫 get(key) 取出物件,若物件不存在則會呼叫 create() 產生物件,並將新產生的物件加入 head of cache 再將其回傳。
  • 加入新物件時,cache 會檢查是否超過指定的大小,若超過則會刪除最後一筆 (tail of cache list)。

// override 這個 method 可以讓我們決定 cache 中每個物件 size 怎麼計算,default returns 1。
@Override
protected int sizeOf( String key, Bitmap value )
{
    return value.getByteCount();
}

Note:
Bitmap#getByteCount()
  • Supported from API Level 12。
// 建構子中則傳入想預設此 cache 的大小,這裡設定最大為 5 M。
// 所以一旦超過 5 M,物件會從最後一直被移除直到總大小小於 5 M。
public LruMemoryCache(Context context)
{
    super( 5 * 1024 * 1024 );
    this.context = context;
}

為避免發生 OutOfMemoryError,在系統呼叫 onLowMemory() 時建議呼叫 LruCache#evictAll() 移除所有元素再重建。

Note:
  • onLowMemory() 會被呼叫是因為整個系統的空間不足而不是因為你的 App 所用的空間已不足!
    • 所以等到此 method 被呼叫時已來不及,更好的方式是在 App 執行一開始便設定適當的大小限制給它。
    • ActivityManager#getMemoryClass() (API Level 5) 可以取得該 application 在 該 device 中的分配空間:
      • 單位為 megabytes。
      • 預設為 16 M,空間大點裝置可到 24 或更高。

也可以利用 LruCache 來管理在 SD card 中的檔案大小:
  • 儲存 File object 在 LruCache(override create())。
  • 以 file.length() 計算檔案大小 (override sizeOf())。
  • 移除物件時刪除 SD card 中的檔案 (override entryRemoved())。


* Reference
- Understanding Weak References | Java.net
- Styling Android » Blog Archive » Memory Cache – Part 1
- Styling Android » Blog Archive » Memory Cache – Part 2
- Styling Android » Blog Archive » Memory Cache – Part 3
- Styling Android » Blog Archive » Memory Cache – Part 4
- WeakHashMap的神话 - - ITeye技术网站
- WeakHashMap是如何清除不用的key的

沒有留言:

張貼留言