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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章