Android 高效載入大圖片避免OOM

來源:互聯網
上載者:User

Android 高效載入大圖片避免OOM

 

我們項目中經常會載入圖片,有時候如果載入圖片過多的話,小則導致程式很卡,重則OOM導致App掛了,今天翻譯https://developer.android.com/training/displaying-bitmaps/index.html,學習Google高效載入大圖片的方法。

圖片有各種形狀和大小,但在大多數情況下,這些圖片都會大於我們程式所需要的大小。比如說系統圖片庫裡展示的圖片大都是用手機網路攝影機拍出來的,這些圖片的解析度會比我們手機螢幕的解析度高得多。大家應該知道,我們編寫的應用程式都是有一定記憶體限制的,程式佔用了過高的記憶體就容易出現OOM(OutOfMemory)異常。我們可以通過下面的代碼看出每個應用程式最高可用記憶體是多少。

 

[java] view plaincopyprint?
  1. int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024/1024);

    現在大部分手機都是32M,既然知道了每個app分配的記憶體,所以就要計算好載入多少圖片而不導致出現OOM,所以要計算每張圖片所佔用的記憶體是多少。

    Android中計算一張圖片所佔記憶體大小方法:圖片長*寬*所佔像素位元組數,而像素位元組數Android中也就四種,

    1:ALPHA_8 佔1個位元組

    2:ARGB_4444 佔2個位元組

    3:ARGB_8888 佔4個位元組

    4:RGB_565 佔2個位元組

     

    :ARGB指的是一種色彩模式,裡面A代表Alpha,R表示red,G表示green,B表示blue,其實所有的可見色都是紅綠藍組成的,所以紅綠藍又稱為三原色。 A  R  G  B
    透明度 紅色 綠色 藍色

    而這些位元組數是可以通過bitmap對象去設定的,bitmap.setConfig(Bitmap.Config.ARGB_4444);如果這是個定值,那麼要改變一個張圖片的大小,就只能改寬或者高了,如果只改高,寬不變的話,就會造成圖片變形,因此一般都是一起改動,所以圖片要縮放,而縮放時根據螢幕的寬和高來縮放的,因為Android裝置很多,每個螢幕的寬和高也不一樣,這樣就能達到載入大圖片避免OOM。

    [java] view plaincopyprint?
    1. BitmapFactory這個類提供了多個解析方法(decodeByteArray, decodeFile, decodeResource等)用於建立Bitmap對象,我們應該根據圖片的來源選擇合適的方法。比如SD卡中的圖片可以使用decodeFile方法,網路上的圖片可以使用decodeStream方法,資源檔中的圖片可以使用decodeResource方法。這些方法會嘗試為已經構建的bitmap分配記憶體,這時就會很容易導致OOM出現。為此每一種解析方法都提供了一個可選的BitmapFactory.Options參數,將這個參數的inJustDecodeBounds屬性設定為true就可以讓解析方法禁止為bitmap分配記憶體,傳回值也不再是一個Bitmap對象,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。這個技巧讓我們可以在載入圖片之前就擷取到圖片的長寬值和MIME類型,從而根據情況對圖片進行壓縮。如下代碼所示: [java] view plaincopyprint?
      1. BitmapFactory.Options options = new BitmapFactory.Options();
      2. options.inJustDecodeBounds = true;
      3. BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
      4. int imageHeight = options.outHeight;
      5. int imageWidth = options.outWidth;
      6. String imageType = options.outMimeType; 比如一張480*800的圖片直接載入到記憶體中,如果圖片很多那就很容易造成OOM,那就必須壓縮,比如按1/8進行壓縮,壓縮後得到的60*100,如果從伺服器擷取的圖片規格不一樣,那壓縮後現在在ImageView上肯定不一樣,因為ImageView寬和高肯定是設定成wrap_content。

        現在寫個Demo縮放圖片代碼如下

         

        [java] view plaincopyprint?
        1. package com.jackie.bitmapdemo;
        2.  
        3. import android.app.Activity;
        4. import android.content.res.Resources;
        5. import android.graphics.Bitmap;
        6. import android.graphics.BitmapFactory;
        7. import android.os.Bundle;
        8. import android.widget.ImageView;
        9.  
        10. public class MainActivity extends Activity {
        11. private ImageView imageView;
        12. @Override
        13. protected void onCreate(Bundle savedInstanceState) {
        14. super.onCreate(savedInstanceState);
        15. setContentView(R.layout.activity_main);
        16. imageView = (ImageView) findViewById(R.id.iv);
        17. Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.b, 60, 100);
        18. iv.setImageBitmap(bitmap);
        19. }
        20.  
        21. public static int calculateInSampleSize(BitmapFactory.Options options,
        22. int reqWidth, int reqHeight) {
        23. // 源圖片的高度和寬度
        24. final int height = options.outHeight;
        25. final int width = options.outWidth;
        26. int inSampleSize = 1;
        27. if (height > reqHeight || width > reqWidth) {
        28. // 計算出實際寬高和目標寬高的比率
        29. final int heightRatio = Math.round((float) height / (float) reqHeight);
        30. final int widthRatio = Math.round((float) width / (float) reqWidth);
        31. //計算縮放比例
        32. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        33. }
        34. return inSampleSize;
        35. }
        36. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        37. int reqWidth, int reqHeight) {
        38. // 第一次解析將inJustDecodeBounds設定為true,來擷取圖片大小
        39. final BitmapFactory.Options options = new BitmapFactory.Options();
        40. options.inJustDecodeBounds = true;
        41. BitmapFactory.decodeResource(res, resId, options);
        42. // 調用上面定義的方法計算inSampleSize值
        43. options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        44. // 使用擷取到的inSampleSize值再次解析圖片
        45. options.inJustDecodeBounds = false;
        46. return BitmapFactory.decodeResource(res, resId, options);
        47. }
        48.  
        49. } 計算縮放比例一般有2種做法:

           

          1:根據螢幕寬和高來縮放圖片

          2:根據要縮放後的寬和高來縮放,上面的例子就是根據這種。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.