android圖片加載處理

在Android 2.3.3(API Level 10)以及以前,Bitmap的backing pixel 數據存儲在native memory, 與Bitmap自己是分開的,Bitmap自己存儲在dalvik heap 中。致使其pixel數據不能判斷是否還須要使用,不能及時釋放,容易引發OOM錯誤。 從Android 3.0(API 11)開始,pixel數據與Bitmap一塊兒存儲在Dalvik heap中。java

結論:android

        如何處理圖片來避免OOM異常:
算法

1.在Android 2.3.3以及以前,建議使用Bitmap.recycle()方法,及時釋放資源。數組

2.設置Options.inPreferredConfig值來下降內存消耗 //如把默認值ARGB_8888改成RGB_565,節約一半內存緩存

3.設置Options.inSampleSize 對大圖片進行壓縮 
ide

4.設置Options.inPurgeable和inInputShareable:讓系統能及時回收內存。
優化

1)inPurgeable:設置爲True時,表示系統內存不足時能夠被回 收,設置爲False時,表示不能被回收。url

2)inInputShareable:設置是否深拷貝,與inPurgeable結合使用,inPurgeable爲false時,該參數無心義True:  share  a reference to the input data(inputStream, array,etc) 。 False :a deep copy。spa

5.使用decodeStream代替其餘decodeResource,setImageResource,setImageBitmap等方法:code

//decodeStream直接調用 JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,也不使用java空間進行分辨率適配,雖節省dalvik內存,但須要在hdpi和mdpi,ldpi中配置相應的 圖片資源,不然在不一樣分辨率機器上都是一樣大小(像素點數量)。其餘方法如setImageBitmap、setImageResource、 BitmapFactory.decodeResource在完成decode後,最終都是經過java層的 createBitmap來完成的,須要消耗更多內存。

在android機器上處理圖片時候,常常會遇到各類各樣的狀況致使out of memory錯誤,這種狀況比通常的exception難處理的多,由於你要爲本身所使用的每一分內存負責。遇到這種狀況時候,不能慌,只能慢慢抽繭剝絲一點點處理。

在此次處理過程當中,我整理了一下幾個處理的思路,應該能夠減輕部分的oom症狀。

一、從讀入內存入手。

在讀入圖片時候能夠給BitmapFactory設置各類參數,使得咱們可以對讀入的圖片尺寸作出控制。

            FileInputStream fis;

                fis = new FileInputStream(path);

                int size = fis.available();

                BitmapFactory.Options options = new BitmapFactory.Options();

                options.inJustDecodeBounds = false;

                options.inPurgeable = true;

                options.inInputShareable = true;

                options.inPreferredConfig = Bitmap.Config.RGB_565;

                if (size > 409600 * 4)

                {

                    options.inSampleSize = (int)Math.sqrt(size / 409600);

                    bitmap = BitmapFactory.decodeStream(fis, null, options);

                }

                else

                {

                    bitmap = BitmapFactory.decodeStream(fis, null, options);

                }

步驟:


i、先獲取圖片大小,而後根據圖片大小來設置不一樣的縮放比例,因此使用了FileInputStream,能夠獲取圖片的大小。


ii、設置這兩個東西inPurgeable 和inInputShareable 。這裏對inPurgeable作個說明。

  1. 若是 

    inPurgeable 

    設爲True的話表示使用BitmapFactory建立的Bitmap 

    用於存儲Pixel的內存空間在系統內存不足時能夠被回收, 在應用須要再次訪問Bitmap的Pixel時(如繪製Bitmap或是調用getPixel),系統會再次調用BitmapFactory 

    decoder從新生成Bitmap的Pixel數組。 爲了可以從新解碼圖像,bitmap要可以訪問存儲Bitmap的原始數據。 

  2. 在inPurgeable爲false時表示建立的Bitmap的Pixel內存空間不能被回收,  

    這樣BitmapFactory在不停decodeByteArray建立新的Bitmap對象,不一樣設備的內存不一樣,所以可以同時建立的Bitmap個數可能有所不一樣, 200個bitmap足以使大部分的設備從新OutOfMemory錯誤。 

     

    當isPurgable設爲true時,系統中內存不足時, 能夠回收部分Bitmap佔據的內存空間,這時通常不會出現OutOfMemory 

    錯誤。

因此設置inPurgeable = true是頗有必要的。這個inInputShareable是和inPurgeable 搭配使用的。

iii、設置讀取色彩。

android中有4中色彩。

ALPHA_8:每一個像素佔用1byte內存 

ARGB_4444:每一個像素佔用2byte內存 
ARGB_8888:每一個像素佔用4byte內存 

RGB_565:每一個像素佔用2byte內存

Android默認的顏色模式爲ARGB_8888,因此咱們採用inPreferredConfig = Bitmap.Config.RGB_565;方式讀取,能夠減小內存消耗,失真在手機查看的狀況下都可以忍受。

iiii、讀取縮略圖進入內存。由於大圖片在手機上處理不須要徹底展現,因此不須要加載所有。我這裏使用了1.6m做爲壓縮的壓縮的基本尺寸,低於這個就不壓縮直接讀取。使用這個尺寸由於須要圖片大小至少爲640*640,對於圖片大小沒有限制的能夠採用更小的尺寸做爲壓縮的基本尺寸。options.inSampleSize這個參數能夠控制讀取的壓縮比例。官方推薦的是使用2的冪次方比例,我這裏也只是使用了普通的壓縮比例。這個須要注意的是這個壓縮是針對寬和高的壓縮,因此對大小是壓縮比例的平方。

另外,這裏吐槽一下,開始使用的是0.8m,結果小米的自帶相機優化,使得按照200k大小取縮略圖後寬高居然不夠640*640了,其餘的手機都是ok的,只能改成1.6m了。



小結:爲了可以讓系統可以處理咱們的圖片,這裏費盡心機的壓縮讀入的圖片尺寸,固然仍是須要在保證需求的前提下的。

二、從使用內存入手

i、及時刪除不用的bitmap。想要儘可能少的使用用內存,要保證在每一個bitmap不使用的時候及時mBitmap.recycle();System.gc();

這個時候要保證這個bitmap是不會再被使用的,也就是新的副本已經被createBitmap或者copy出來了。否則的話,你隨便的recycle會致使當前正在使用的圖片也會被回收,頁面上就沒圖片了,嚴重的還會致使系統在調用圖片時候崩潰。因此之一部須要很細緻。

後面的這個system.gc()只是通知虛擬機能夠回收了,可是虛擬機何時回收還不必定,因此不要寄但願於這個東西會立馬清出你須要的內存。

這裏能夠經過DDMS裏面查看應用的heap使用狀況來確認你的bitmap處理是否正常。

三、從圖片存儲和重複利用入手

i、使用內存緩存和文件緩存處理。由於從內存讀取圖片會比較快,因此爲了更大限度使用內存,使用兩層緩存。硬引用緩存不會輕易被回收,用來保存經常使用數據,不經常使用的轉入軟引用緩存。

private static final int SOFT_CACHE_SIZE = 15;  //軟引用緩存容量

    private static LruCache mLruCache;  //硬引用緩存

    private static LinkedHashMap> mSoftCache;  //軟引用緩存

                                                                                          

    public ImageMemoryCache(Context context) {

   

        int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();

        int cacheSize = 1024 * 1024 * memClass / 4;  //硬引用緩存容量,爲系統可用內存的1/4

        mLruCache = new LruCache(cacheSize) {

            @Override

            protected int sizeOf(String key, Bitmap value) {

                if (value != null)

                    return value.getRowBytes() * value.getHeight();

                else

                    return 0;

            }

                                                                                          

            @Override

            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {

                if (oldValue != null)

                    // 硬引用緩存容量滿的時候,會根據LRU算法把最近沒有被使用的圖片轉入此軟引用緩存

                    mSoftCache.put(key, new SoftReference(oldValue));

            }

        };

        mSoftCache = new LinkedHashMap>(SOFT_CACHE_SIZE, 0.75f, true) {

            private static final long serialVersionUID = 6040103833179403725L;

            @Override

            protected boolean removeEldestEntry(Entry> eldest) {

                if (size() > SOFT_CACHE_SIZE){    

                    return true;  

                }  

                return false; 

            }

        };

    }

                                                                                  

   

    public Bitmap getBitmapFromCache(String url) {

    if(url==null){

    return null;

    }

        Bitmap bitmap;

        //先從硬引用緩存中獲取

        synchronized (mLruCache) {

            bitmap = mLruCache.get(url);

            if (bitmap != null) {

                //若是找到的話,把元素移到LinkedHashMap的最前面,從而保證在LRU算法中是最後被刪除

                mLruCache.remove(url);

                mLruCache.put(url, bitmap);

                return bitmap;

            }

        }

        //若是硬引用緩存中找不到,到軟引用緩存中找

        synchronized (mSoftCache) { 

            SoftReference bitmapReference = mSoftCache.get(url);

            if (bitmapReference != null) {

                bitmap = bitmapReference.get();

                if (bitmap != null) {

                    //將圖片移回硬緩存

                    mLruCache.put(url, bitmap);

                    mSoftCache.remove(url);

                    return bitmap;

                } else {

                    mSoftCache.remove(url);

                }

            }

        }

        return null;

    } 

                                                                                  

   

    public void addBitmapToCache(String url, Bitmap bitmap) {

        if (bitmap != null) {

            synchronized (mLruCache) {

                mLruCache.put(url, bitmap);

            }

        }

    }

                                                                                  

    public void clearCache() {

        mSoftCache.clear();

    }

這個都有註釋了,應該均可以看得懂。文件方面就和普通的同樣了。

四、從頁面優化入手

i、使用viewstub替換掉常用的view.visible,viewstub在不顯示的時候會不佔用內存,另一個會佔用內存。這樣能夠減小圖片佔用的內存使用,一樣適用於其餘的內存消耗。

忘記從哪位高人轉載的了,若有侵權請告知

相關文章
相關標籤/搜索