Android如何高效加載大圖

今天的學習目標是如何高效的加載大圖
衆所周知,android 在加載數量很多的大圖的時候,容易引起OOM異常(內存溢出),這是爲什麼呢?是因爲現在的手機圖片的分辨率越來越高,圖片越來越多,而系統給單個應用施加了內存限制,比如16MB,這就導致加載bitmap的時候,很容易就內存溢出了。

bitmap相關知識介紹

bitmap表示的是位圖,也就是圖片,獲取bitmap的方法是什麼呢?
BitmapFactory提供了

decodeStream(); 
decodeResource(); 
decodeFile(); 
decodeByteArray();

這樣的四個方法,分別表示在輸入流,資源文件,文件,和字節數組中加載一個bitmap對象。相信這些方法大家一定使用過。

高效加載圖片的思路

那麼怎麼高效的加載一張圖片呢?比如一張1920*1080的圖片,僅僅只是放在一個128*96的imageview控件裏面,如果直接把圖片放到控件裏面,那麼系統還是會把整個圖片加載到內存中,再賦給imageview,這樣就顯的很壕。所以我們要做的就是按比例縮小圖片,只在內存中加載縮小後的圖片,圖片被縮小後當然就沒有原始圖片那麼大了,對內存的佔用量,也就沒有那麼大了。

加載圖片的方法

那麼問題來了。

  1. 什麼時候縮小
  2. 怎麼縮小

我們來一一解答這些問題:
我們在縮小圖片前要做一個判斷,判斷這張圖片是否應該縮小,判斷依據是什麼呢,首先,我們先獲取圖片的大小,再和imageview所設置的大小做對比,如果圖片的寬和高都大於我們所規定的大小,這當然得縮小,那如果圖片比規定大小隻大那麼一丟丟,應該怎麼辦呢,這個我們後面會討論。
首先要解決的問題是怎麼獲取圖片的大小,在BitmapFactory裏面有一個內部類Options,該類的屬性可以控制decodeResource()這些方法的行爲。比如inJustDecodeBounds這個屬性,這個屬性爲boolean類型的,當該值爲true的時候,decodeResource()所返回的bitmap是null,但是可以獲取到圖片的outWidth, outHeight 與 outMimeType,所以:

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;

獲取到圖片的尺寸了,判斷也就容易了,那麼我們怎麼縮小圖片呢?
在Options裏面有一個inSampleSize,是一個int型的值,假設我們現在有一張1000*1000的圖片,如果inSampleSize設爲2,表示長寬統統除以2,縮小後的圖片便是500*500,面積就爲原來的1/4,內存自然也就只有原來的1/4了,本來10MB的圖片,現在瞬間變2.5MB了,如果inSampleSize設爲4,那縮小後的圖片就只有0.625MB,內存終於保住了有木有!!具體怎麼做呢,額,我們還是先根據圖片大小和規定大小比對一下,算出最適合的inSampleSize好了,怎麼算呢?

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

通過該方法,我們會發現,獲取的數字是1、2、4、8、16之類的數字,都是以2爲指數的冪,這是官方規定的。

既然已經獲取到了inSampleSize,那麼是時候獲取圖片了,先將Options的inJustDecodeBounds值設置爲true,再將options傳到decodeResource()裏面,這樣的options就可以獲取到圖片的大小了,然後計算出inSampleSize的大小,再將inJustDecodeBounds設置爲false,真正的獲取bitmap:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

使用方法就顯而易見了。

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

爲了對比效果,我做了個小例子,例子很簡單,佈局裏面一個128*96的imageview圖片,然後把drawable裏面的圖片賦給imageview,結果效果驚人。

普通設置方法:

img.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.img));

內存使用量:
這裏寫圖片描述

高效加載方法:

img.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.drawable.img,128,96));

內存使用量:
這裏寫圖片描述

以上就是高效加載大圖的方式,你學會了嗎?

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