使得內存的使用更為合理,限制每個應用的可用內存上限,可以防止某些應用程式惡意或者無意使用過多的內存,而導致其他應用無法正常運行,我們眾所周知的Android是有多進程的,如果一個進程(也就是一個應用)耗費過多的內存,其他的應用還搞毛呢?當然在這裏其實是有一個例外,那就是如果你的應用使用了很多本地代碼,在本地代碼中創建對象解碼圖像是不會被計算到的,這是因為你使用本地方法創建的對象或者解碼的圖像使用的是本地堆的內存,跟系統是平級的,而我們通過Framework調用BitmapFactory.decodeFile()方法解碼時,系統雖然也是調用本地代碼來進行解碼的,但是Android Framework在實現的時候,刻意地將這部分解碼使用的內存從堆裏面分配了而不是從本地堆裏分配的內存,所以才會出現OOM,當然並不是說從本地堆裏分配就不會出現OOM,本地堆分配內存超過系統可用內存限制的話,通常都是直接崩潰,什麼錯誤可能都看不到,也許會有一些崩潰的錯誤字節碼之類的。
省電的考慮,呃...,原因我好像也不能很明白地說出來。
回到正題來,我們在應用的設計和開發中可能會經常碰到需要在一個介面上顯示數十張圖片乃至上百張,當然限於手機螢幕的大小我們通常在設計中會使用類似於清單或者網格的控制項來展示,也就是說通常一次需要顯示出來圖片數還是一個相對確定的數字,通常也不會太大。如果數目比較大的畫,通常顯示的控制項自身尺寸就會比較小,這個時候可以採用縮略圖策略。下面我們來看看如果避免出現OOM的錯誤,這個解決方案參考了Android示範程式XML Adapters中的ImageDownloader.java中的實現,主要是使用了一個二級緩存類似的機制,就是有一個數據結構中直接持有解碼成功的Bitmap對象引用,同時使用一個二級緩存數據結構持有解碼成功的Bitmap對象的SoftReference對象,由於SoftReference對象的特殊性,系統會在需要內存的時候首先將SoftReference對象持有的對象釋放掉,也就是說當VM發現可用內存比較少了需要觸發GC的時候,就會優先將二級緩存中的Bitmap回收,而保有一級緩存中的Bitmap對象用於顯示。
其實這個解決方案最為關鍵的一點是使用了一個比較合適的數據結構,那就是LinkedHashMap類型來進行一級緩存Bitmap的容器,由於LinkedHashMap的特殊性,我們可以控制其內部存儲對象的個數並且將不再使用的對象從容器中移除,這就給二級緩存提供了可能性,我們可以在一級緩存中一直保存最近被訪問到的Bitmap對象,而已經被訪問過的圖片在LinkedHashMap的容量超過我們預設值時將會把容器中存在時間最長的對象移除,這個時候我們可以將被移除出LinkedHashMap中的對象存放至二級緩存容器中,而二級緩存中對象的管理就交給系統來做了,當系統需要GC時就會首先回收二級緩存容器中的Bitmap對象了。在獲取對象的時候先從一級緩存容器中查找,如果有對應對象並可用直接返回,如果沒有的話從二級緩存中查找對應的SoftReference對象,判斷SoftReference對象持有的Bitmap是否可用,可用直接返回,否則返回空。
主要的代碼段如下: