Android之Bitmap總結(jié)(加載、尺寸壓縮、優(yōu)化)

關(guān)于Bitmap加載

Bitmap的加載離不開BitmapFactory類,BitmapFactory類提供了四類方法用來加載Bitmap:

  • 第一種: BitmapFactory.decodeFile,從文件系統(tǒng)加載
    a. 通過Intent打開本地圖片或照片
    b. 在onActivityResult中獲取圖片uri
    c. 根據(jù)uri獲取圖片的路徑
    d. 根據(jù)路徑解析bitmap:Bitmap bm = BitmapFactory.decodeFile(sd_path)
  • 第二種:BitmapFactory.decodeResource,以R.drawable.xxx的形式從本地資源中加載
    Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.aaa);
  • 第三種:BitmapFactory.decodeStream,從輸入流加載
    a.開啟異步線程去獲取網(wǎng)絡(luò)圖片
    b.網(wǎng)絡(luò)返回InputStream
    c.解析:Bitmap bm = BitmapFactory.decodeStream(stream),這是一個耗時操作,要在子線程中執(zhí)行
  • 第四種:BitmapFactory.decodeByteArray, 從字節(jié)數(shù)組中加載
    a.開啟異步線程去獲取網(wǎng)絡(luò)圖片
    b.網(wǎng)絡(luò)返回InputStream
    c. 把InputStream轉(zhuǎn)換成byte[]
    d. 解析:Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);

關(guān)于Bitmap的優(yōu)化

提到bitmap,我們通常會聯(lián)想到內(nèi)存溢出,主要的原因就是我們加載的圖片內(nèi)存過大以及每個應(yīng)用的內(nèi)存有限。所以我們經(jīng)常會看到報這種錯誤:

Bitmap too large to be uploaded into a texture (3120x4160, max=4096x4096)

Caused by: java.lang.OutOfMemoryError: Failed to allocate a 144764940 byte allocation with 16765264 free bytes and 109MB until OOM

一、Bitmap優(yōu)化之高效加載---尺寸壓縮

主要的做法就是使用系統(tǒng)提供給我們Options類來處理Bitmap。
通過BitmapFactory.Options按一定的采樣率來加載縮小后的圖片,然后在ImageView中使用縮小的圖片這樣就會降低內(nèi)存占用避免【OOM】,提高了Bitamp加載時的性能。
這其實就是我們常說的圖片壓縮方案之尺寸壓縮。
尺寸壓縮是壓縮圖片的像素,一張圖片所占內(nèi)存的大小 =【圖片類型】(ALPHA_8/ARGB_4444/ARGB_8888/RGB_256)*【寬】*【高】,通過改變?nèi)齻€值減小圖片所占的內(nèi)存,防止OOM,當(dāng)然這種方式可能會使圖片失真 。

對于上面提到的圖片類型,也就是android 色彩模式說明:

  1. ALPHA_8:每個像素占用1byte內(nèi)存。
  2. ARGB_4444:每個像素占用2byte內(nèi)存
  3. ARGB_8888:每個像素占用4byte內(nèi)存
  4. RGB_565:每個像素占用2byte內(nèi)存
    而android默認(rèn)的色彩模式就是ARGB_8888,質(zhì)量最高,同時內(nèi)存也是最大。效果也肯定是最好的。

假設(shè)一張10241024,模式為ARGB_8888的圖片,那么它占有的內(nèi)存就是:10241024*4 = 4MB

Options類的inPreferredConfig

用這個改變內(nèi)存大小的三個值得第一個。圖片類型,指定為更小的色彩模式,可以讓圖片的內(nèi)存變小。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig= Bitmap.Config.RGB_565;

Options類的inSampleSize采樣率

options.inPreferredConfig=2;

通過采樣率改變內(nèi)存大小的三個值中的寬和高這兩個值,采樣率同時作用于寬和高。
以下有幾點注意事項:

  • 當(dāng)inSampleSize=1時,采樣后的圖片為圖片的原始大小。
  • 除了1以外,inSampleSize的取值應(yīng)該總為2的整數(shù)倍,否則會【向下取整】,取一個最接近2的整數(shù)倍,比如inSampleSize=3時,系統(tǒng)會取inSampleSize=2
  • 當(dāng)inSampleSize=2時,采樣后的圖片的寬高均為原始圖片寬高的1/2

假設(shè)一張10241024,模式為ARGB_8888的圖片,inSampleSize=2,原始占用內(nèi)存大小是4MB,采樣后的圖片占用內(nèi)存大小就是(1024/2) * (1024/2 ) 4 = 1MB

我們用一個小例子來驗證上述講的:

代碼如下:

public class MainActivity extends AppCompatActivity {
    ImageView image;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main_test);
        image= (ImageView) findViewById(R.id.image);
        //獲取我們要顯示的圖片
        Bitmap bm = decodeBitmapFromResource();
        //通過imageview顯示出來
        image.setImageBitmap(bm);
    }

    //在這個方法里我們對圖片做處理
    private Bitmap decodeBitmapFromResource(){

        Log.d("log","bitmap原始圖片內(nèi)存大?。?+BitmapFactory.decodeResource(getResources(), R.drawable.tupian).getAllocationByteCount());
        //獲取  BitmapFactory.Options的實例
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig= Bitmap.Config.RGB_565;
        //inJustDecodeBounds參數(shù)設(shè)為true并加載圖片。
        //這樣做的原因是inJustDecodeBounds為true時
        //BitmapFactory只會解析圖片的原始寬高信息,并不會真正的加載圖片。
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(getResources(), R.drawable.tupian, options);
        Log.d("log","原始圖片的寬width:" +options.outWidth+"原始圖片的高h(yuǎn)eight:"+options.outHeight);
//        Log.d("log","bitmap原始圖片內(nèi)存大小:"+ bitmap.getAllocationByteCount());
    //原始圖片的寬高已經(jīng)存放在options中。options.outWidth和options.outHeight
    //我們可以用它去做一些判斷,得到我們想要的采樣率
        //options.inSampleSize = calculateSampleSize(options,300,300);
        options.inSampleSize=2;
    //得到采樣率后,將BitmapFacpry.Options的inSampleSize參數(shù)設(shè)為false并重新加載圖片。
    //此時是真正的加載圖片。
        options.inJustDecodeBounds =false;
        Bitmap newbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.tupian,options);
        Log.d("log","壓縮后圖片的寬width:" +options.outWidth+"壓縮后圖片的高h(yuǎn)eight:"+options.outHeight);
        Log.d("log","bitmap壓縮后圖片內(nèi)存大小:"+ newbitmap.getByteCount());
        return  BitmapFactory.decodeResource(getResources(),R.drawable.tupian,options);
    }
}
原始圖片

log.輸出

根據(jù)輸出,我們可得到?jīng)]有對圖片處理前圖片類型:

1411200/600/588=4;也就是android默認(rèn)的色彩模式就是ARGB_8888,4byte

而代碼中我們通過設(shè)置這兩個值

options.inPreferredConfig= Bitmap.Config.RGB_565;
options.inSampleSize=2;

使得類型為RGB_565--2byte,同時寬高同時為原來的1/2,此時圖片的內(nèi)存為=2* 300 *294=176400;


二、Bitmap優(yōu)化之日常使用注意事項

【1】對于不再使用的bitmap,要及時清理回收,Activity的onStop()或者onDestroy()方法中進(jìn)行回收: bitmap.recycle(); //回收圖片所占的內(nèi)存
【2】捕獲OutOfMemoryError
因為Bitmap非常耗內(nèi)存,了避免應(yīng)用在分配Bitmap內(nèi)存的時候出現(xiàn)OutOfMemory異常以后Crash掉,需要特別注意實例化Bitmap部分的代碼。通常,在實例化Bitmap的代碼中,一定要對OutOfMemory異常進(jìn)行捕獲。很多開發(fā)者會習(xí)慣性的在代碼中直接捕獲Exception。但是對于OutOfMemoryError來說,這樣做是捕獲不到的。因為OutOfMemoryError是一種Error,而不是Exception。
Bitmap bitmap = null;
    try {
        // 實例化Bitmap
        bitmap = BitmapFactory.decodeFile(path);
    } catch (OutOfMemoryError e) {

    }
    if (bitmap == null) {
        return defaultBitmapMap; // 如果實例化失敗 返回默認(rèn)的Bitmap對象
    }
【3】不重復(fù)創(chuàng)建相同的bitmap
如果不進(jìn)行緩存,盡管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實例化出來的Bitmap,是不同的Bitmap對象。緩存可以避免新建多個Bitmap對象,避免內(nèi)存的浪費。

如果我們的程序中要創(chuàng)建多個bitmap,比如某個場景,我們要在屏幕上灑各式各樣的花瓣,但這其中總會有重復(fù)的,這里我們就可以使用一個hashmap,對每個bitmap以一個值作為標(biāo)識,然后在創(chuàng)建函數(shù)中進(jìn)行判斷,

//簡易代碼,大概表達(dá)思想:
 HashMap<Integer, Bitmap> bitmapMap = new HashMap<Integer, Bitmap>();
 static Bitmap createBitmap(int value){
 Bitmap bitmap = bitmapMap.get(value);
        if (bitmap == null) {
           bitmap =BitmapFactory.decodeResource(getResources(), R.drawable.value);
            bitmapMap.put(value, bitmap);
        }
        return bitmap;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容