1. 程式人生 > >android圖片載入導致的OOM分析及有效解決辦法(BitmapUtils)

android圖片載入導致的OOM分析及有效解決辦法(BitmapUtils)

android應用尤其是涉及到很多圖片處理的經常會遇到OOM(Out Of Memory),為什麼會導致OOM,又該如何解決呢?

OOM原因分析:

android每一個應用都有一個獨立的程序,每個程序都是例項化了dalvik虛擬機器例項的linux程序。Dalvik 主要管理的記憶體有 Java heap 和 native heap 兩大塊。Android系統對dalvik的vm heapsize作了硬性限制,當java程序申請的java空間超過閾值時,就會丟擲OOM異常(這個閾值可以是48M、24M、16M等,視機型而定),而native heap大小則是不受次限制的。

當我們需要顯示大的bitmap物件或者較多的bitmap的時候,如果使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設定圖片,則容易出現OOM,這個是因為bitpmap分配記憶體受限於java heap大小(一張在1024*1024圖片,假設照片是用ARGB_8888格式,那麼需要佔用4M的記憶體儲存畫素點資訊, bitmap解析度越高,所佔用的記憶體就越大,這個是以2為指數級增長的。)。此處引出一個問題:bitmap物件是分配在native heap還是java heap上的呢?後續會在單獨一篇文章中講解(

Bitmap分配在java heap還是native heap)

解決圖片引起的OOM思路

(1)大的圖片佔用記憶體會非常大,但是在一個很小的ImageView上顯示一張超大的圖片不會帶來任何視覺上的好處,但卻會佔用我們相當多寶貴的記憶體,而且在效能上還可能會帶來負面影響,可以給ImageView設定比原圖要小的合理尺寸的bitmap。(具體的實現方式可以在閱讀完其他部分再細讀)
BitmapFactory

檢視BitmapFactory介面會發現,多個介面要求傳入Options物件,options支援設定的引數如上圖,其中有兩個比較重要的引數:inJustDecodeBounds和inSampleSize

    /**
         * If set
to true, the decoder will return null (no bitmap), but * the out... fields will still be set, allowing the caller to query * the bitmap without having to allocate the memory for its pixels. */ public boolean inJustDecodeBounds; /** * If set to a value > 1
, requests the decoder to subsample the original * image, returning a smaller image to save memory. The sample size is * the number of pixels in either dimension that correspond to a single * pixel in the decoded bitmap. For example, inSampleSize == 4 returns * an image that is 1/4 the width/height of the original, and 1/16 the * number of pixels. Any value <= 1 is treated the same as 1. Note: the * decoder will try to fulfill this request, but the resulting bitmap * may have different dimensions that precisely what has been requested. * Also, powers of 2 are often faster/easier for the decoder to honor. */ public int inSampleSize;

inJustDecodeBounds引數可以讓我們在不對圖片解碼的情況下直接獲取到圖片的大小,非常方便高效;

inSampleSize引數則是用於設定生成的bitmap的大小,大於1時要求生成的bimap只有原圖1/inJustDecodeBounds大小,建議使用2、4、8等2指數值,對於解碼將容易和簡單。

(2)如果一個ScrollView中有很多圖片,即使使用上述方法,記憶體中也會有很多bitmap物件,依然可能會發生OOM。但是Scrollview中的圖片只是在顯示的時候才需要載入,可以通過快取方式將這些已經載入的bitmap快取起來,當有新的bitmap需要載入時且超過快取總容量時將暫時不使用的bimap回收,控制記憶體佔用,防止OOM。

現有的圖片載入開源框架

BitmapUtils主要解決Android載入圖片出現的OOM問題,採取了多級快取機制(記憶體快取和磁碟快取)儲存圖片避免OOM現象,採取非同步載入bitmap,在listView快速滑動時停止載入。

  1. BitmapUtils介面支援Assets、Url、Path的圖片路徑。
    BitmapGlobalConfig主要配置磁碟快取路徑,程式記憶體快取大小。(不設定使用預設值),直接在bitmapUtils初始化時設定。
    BitmapDisplayConfig主要配置非同步載入未完成時臨時顯示的圖片,載入失敗顯示的圖片,載入過程中的動畫,圖片是否翻轉,是否顯示原圖以及顯示圖片的最大Size等。

  2. BitmapUtils支援記憶體快取(LruCache)和磁碟快取(LruDiskCache)
    記憶體快取主要採取LinkedHashMap< MemoryCacheKey, Bitmap >儲存bitmap到快取中,預設系統快取為4M,當記憶體超出4M,移除上一元素的快取。磁碟快取的目的是xxx

  3. BitmapUtils載入Bitmap策略
    (1) 首先載入記憶體快取bitmap。
    (2) 在記憶體快取獲取不到的情況下,載入檔案快取的檔案輸入流,把檔案輸入流轉化為bitmap,同時儲存到記憶體快取中,並且回收當前的bitmap。
    (3) 直接載入路徑下的資源(Assets、Path、URL)處理都一樣,都是獲取檔案輸入流。通過BitmapFactory.decodeFileDescriptor,把檔案輸入流轉化為bitmap,同時儲存檔案輸入流到檔案快取中,儲存bitmap到記憶體快取中,回收當前的bitmap。