Android大圖載入記憶體最佳化(如何防止OutOfMemory)

來源:互聯網
上載者:User

標籤:

一、簡介

行動裝置不斷髮展的今天,有的人認為記憶體已經足夠大了,不用再管什麼記憶體最佳化,Java是虛擬機器可以幫我維護記憶體。其實記憶體空間資源還是很寶貴的,不管手機記憶體有多大,系統分配給單個應用的記憶體空間還是很有限的大致有16M,64M,128M等。在Android中載入大圖會非常消耗系統資源,16M的圖片大致可以儲存3張1024X1536品質為ARGB_8888的圖片,這裡邊還不包含其它Object所佔的資源。軟體在系統上運行,環境是很複雜的,可能測試的時候有限的測試次數上沒有發現記憶體流失問題,但是在成千上萬的使用者使用過程中,總會有各種記憶體泄露問題。

二、圖片所佔記憶體空間的計算規則

大圖載入首先要清楚圖片的品質與所佔記憶體的計算規則:Bitmap.Config

Bitmap.Config

介紹(每個像素點的構成)

1pix所佔空間

1byte = 8位

1024*1024圖片大小

(解析度)

ALPHA_8

只有透明度,沒有顏色,那麼一個像素點佔8位。

1byte

1M

RGB_565

即R=5,G=6,B=5,沒有透明度,那麼一個像素點佔5+6+5=16位

2byte

2M

ARGB_8888

由4個8位組成,即A=8,R=8,G=8,B=8,那麼一個像素點佔8+8+8+8=32位

4byte

4M

ARGB_4444

由4個4位組成,即A=4,R=4,G=4,B=4,那麼一個像素點佔4+4+4+4=16位 

2byte

2M


三、載入和顯示的常見策略1.直接載入並顯示:直接把圖片解碼並顯示載入到控制項上,常用的setImageResource方法就是直接載入圖片到ImageView上邊展示,這種方法是載入原圖的方式;2.載入到記憶體做縮放:解碼到記憶體或者Bitmap,判斷Bitmap的大小判斷是否需要二次處理,縮放到指定的大小再顯示;3.降低品質載入:

解碼的時候設定Bitmap.inPreferredConfig ,嘗試載入不同品質的位元影像到記憶體;

4.預先載入的方式:

就是先擷取將要載入的圖片的大小,預計算記憶體的佔用和是否我們需要這樣解析度的圖片。

四、大圖的載入最佳化

         採用BitmapFactory載入圖片,它提供了decodeResource 和decodeFile兩個方法給我們解碼Resource目錄和本地SDCard目錄下的圖片,提供了強大的BitmapFactory.Options 給我在解碼圖片的過程中的配置。

1.使用decodeResource方法載入Drawable下邊的圖片

圖片解析度為:8176 x 2368 顏色模型是:ARGB_8888, 完全載入到記憶體所需資源是:73.6M左右,意味著巨大多數的Android手機都會瞬間記憶體流失。

載入圖片時先擷取圖片的大小和圖片的格式:將inJustDecodeBounds設定為true,不解碼圖片到記憶體,唯讀取圖片的基本資料。

BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.id.myimage, options);int imageHeight = options.outHeight;int imageWidth = options.outWidth;String imageType = options.outMimeType;

這時候可以擷取到圖片的大小和格式,然後根據需要載入的模型計算所佔記憶體空間的大小:

/** * A helper function to return the byte usage per pixel of a bitmap based on its configuration. */public static int getBytesPerPixel(Bitmap.Config config) {    if (config == Bitmap.Config.ARGB_8888) {        return 4;    } else if (config == Bitmap.Config.RGB_565) {        return 2;    } else if (config == Bitmap.Config.ARGB_4444) {        return 2;    } else if (config == Bitmap.Config.ALPHA_8) {        return 1;    }    return 1;}

根據圖片的寬和高擷取圖片大小:

/** * get the image size in the RAM * * @param imageW * @param imageH * @return */public static long getBitmapSizeInMemory(int imageW, int imageH) {    return imageH * imageW * getBytesPerPixel(Bitmap.Config.ARGB_8888);}

這時候可以增加一些策略,比如只載入控制項大小的圖片,或者判斷一下當前的記憶體使用量情況,在視情況載入圖片:這時候要把inJustDecodeBounds設定為false,這裡我們示範一下按照指定解析度載入圖片的方式:

/** * Load bitmap from resources * * @param res        resource * @param drawableId resource image id * @param imgH       destination image height * @param imgW       destination image width * @return */public static Bitmap loadHugeBitmapFromDrawable(Resources resources, int drawableId, int imgH, int imgW) {    Log.d(TAG, "imgH:" + imgH + " imgW:" + imgW);    BitmapFactory.Options options = new BitmapFactory.Options();    //preload set inJustDecodeBounds true, this will load bitmap into memory    options.inJustDecodeBounds = true;    //options.inPreferredConfig = Bitmap.Config.ARGB_8888;//default is Bitmap.Config.ARGB_8888    BitmapFactory.decodeResource(resources, drawableId, options);    //get the image information include: height and width    int height = options.outHeight;    int width = options.outWidth;    String mimeType = options.outMimeType;    Log.d(TAG, "width:" + width + " height:" + height + " mimeType:" + mimeType);    //get sample size    int sampleSize = getScaleInSampleSize(width, height, imgW, imgH);    options.inSampleSize = sampleSize;    // Decode bitmap with inSampleSize set    options.inJustDecodeBounds = false;    Log.d(TAG, "memory size:" + getBitmapSizeInMemory(width / sampleSize, height / sampleSize));    Bitmap bitmap = BitmapFactory.decodeResource(resources, drawableId, options);    Log.d(TAG, "w=" + bitmap.getWidth() + " h=" + bitmap.getHeight() + " bitmap size:" + bitmap.getRowBytes() * bitmap.getHeight());    return bitmap;}


2.使用decodeFile方法載入SDCard下邊的圖片圖片資訊同上。

/** * load the bitmap from SDCard with the imgW and imgH * * @param imgPath  resource path * @param imgH     result image height * @param imgW     result image width * @return result bitmap */public static Bitmap loadHugeBitmapFromSDCard(String imgPath, int imgH, int imgW) {    Log.d(TAG, "imgH:" + imgH + " imgW:" + imgW);    BitmapFactory.Options options = new BitmapFactory.Options();    //preload set inJustDecodeBounds true, this will load bitmap into memory    options.inJustDecodeBounds = true;    //options.inPreferredConfig = Bitmap.Config.ARGB_8888;//default is Bitmap.Config.ARGB_8888    BitmapFactory.decodeFile(imgPath, options);    //get the image information include: height and width    int height = options.outHeight;    int width = options.outWidth;    String mimeType = options.outMimeType;    Log.d(TAG, "width:" + width + " height:" + height + " mimeType:" + mimeType);    //get sample size    int sampleSize = getScaleInSampleSize(width, height, imgW, imgH);    options.inSampleSize = sampleSize;    // Decode bitmap with inSampleSize set    options.inJustDecodeBounds = false;    Log.d(TAG, "memory size:" + getBitmapSizeInMemory(width / sampleSize, height / sampleSize));    Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);    Log.d(TAG, "w=" + bitmap.getWidth() + " h=" + bitmap.getHeight() + " bitmap size:" + bitmap.getRowBytes() * bitmap.getHeight());    return bitmap;}

兩種方式的原理完全一樣。

3.其中inSampleSize的計算方式和注意點

根據官方文檔我們可以瞭解到,inSampleSize必須是2的指數倍,如果不是2的指數倍會自動轉位教下的2的指數倍的數,下邊我提供一個計算方法:

/**     * get the scale sample size     *     * @param resW resource width     * @param resH resource height     * @param desW result width     * @param desH result height     * @return     */    public static int getScaleInSampleSize(int resW, int resH, int desW, int desH) {        int scaleW = resW / desW;        int scaleH = resH / desH;        int largeScale = scaleH > scaleW ? scaleH : scaleW;        int sampleSize = 1;        while (sampleSize < largeScale) {            sampleSize *= 2;        }        Log.d(TAG, "sampleSize:" + sampleSize);        return sampleSize;    }

接下來,我講繼續講解:圖片載入之圖片緩衝和非同步載入技術。



最後我附上該項目的github地址:https://github.com/CJstar/HugeLocalImageLoad.git


參考:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Android大圖載入記憶體最佳化(如何防止OutOfMemory)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.