高效地加載大Bitmap(位圖)

譯者按: 在Google最新的文檔中,提供了一系列含金量相當高的教程。因爲種種原因而鮮爲人知,真是可惜!Ryan將會細心整理,將之翻譯成中文,希望對開發者有所幫助。

        本系列是Google關於展示大Bitmap(位圖)的官方演示,可以有效的解決內存限制,更加有效的加載並顯示圖片,同時避免讓人頭疼的OOM(Out Of Memory)。

-------------------------------------------------------------------------------------

譯文:

         圖像可以有各種各樣的形狀和大小。在很多情況下,它們往往會比典型的應用UI界面所需要的更大。例如,系統的Gallery程序展示使用Android設備的攝像頭拍攝的照片的分辨率往往要遠高於設備的屏幕密度。 

        考慮到你所使用的內存有限,理想的情況是你只會想加載一個分辨率相對較低的圖片到內存中來。低分辨率版本的圖片與相應UI組件的尺寸應該是相匹配的。一張高分辨率的圖片並不能帶給你任何可見的好處,卻要佔據着寶貴的內存,以及間接導致由於動態縮放引起額外的性能開銷。 

        這節課將向你演示如何解碼大圖片,通過加載較小的圖片樣本以避免超出應用的內存限制。


讀取Bitmap(位圖)的尺寸和類型

        BitmapFactory提供了幾種解碼方式(decodeByteArray(), decodeFile(), decodeResource()等等),以便從多種資源中創建一個Bitmap(位圖)對象。可以根據你的圖片數據來源選擇最合適的解碼方式。這些方法視圖爲構造Bitmap對象分配內存,因此很容易導致OutOfMemory(OOM)異常。每一種解碼方式都有額外的特徵,你可以通過BitmapFactory.Options類指定解碼方法。在解碼圖片的時候設置inJustDecodeBounds屬性爲true,可以避免內存分配,返回的bitmap對象爲null卻可以設置outWidth, outHeight和outMimeType。這項技術允許你在創建Bitmap(並分配內存)之前讀取圖片的尺寸和類型。

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;
        爲了避免java.lang.OutOfMemeory異常,在解碼圖片之前就要檢查圖片的尺寸,除非你十分確信圖片資源的尺寸是可預見的並且有着充裕的可用內存。 

將縮小版的圖片加載到內存中 

        現在圖片的尺寸已經知道了,這些信息可以用來決定是將一個完整尺寸的圖片加載到內存中,還是應該用一個圖片的子樣本來取代它。這裏有一些可供考慮的因素: 
  • 估計加載全尺寸的圖片所要消耗的內存
  • 在考慮應用中其他內存需求的情況下,你願意給加載這個圖片分配的內存空間。
  • 準備加載該圖像的目標ImageView或者UI組件的尺寸
  • 當前設備的屏幕的尺寸和密度

        例如,如果最終只是要在ImageView中顯示一張128*96px大小的縮略圖,直接加載1024*768px的圖片是非常不值得的。

        爲了告訴解碼器如何對圖像進行採樣,加載更小版本的圖片,需要在BitmapFactory.Options對象中將inSampleSize設置爲true。例如,一張分辨率爲2048*1536px的圖像使用inSampleSize值爲4的設置來解碼,產生的Bitmap大小約爲512*384px。相較於完整圖片佔用12M的內存,這種方式只需0.75M內存(假設Bitmap配置爲ARGB_8888)。這裏有一個方法用來計算基於目標高寬的sample size的值:

01 public static int calculateInSampleSize(
02             BitmapFactory.Options options, int reqWidth, int reqHeight) {
03     // Raw height and width of image
04     final int height = options.outHeight;
05     final int width = options.outWidth;
06     int inSampleSize = 1;
07  
08     if (height > reqHeight || width > reqWidth) {
09         if (width > height) {
10             inSampleSize = Math.round((float)height / (float)reqHeight);
11         else {
12             inSampleSize = Math.round((float)width / (float)reqWidth);
13         }
14     }
15     return inSampleSize;
16 }

        提示:使用2的次冪來設置inSampleSize值可以使解碼器執行地更加迅速、更加高效。但是,如果你想在內存或者硬盤上緩存一個調整過大小的圖片,通常還是解碼到合適的圖片尺寸更加節省空間。

        要使用這個方法,首先要使用inJustDecodeBoundstrue來解碼尺寸信息,將options傳遞過去使用新的inSampleSize值再次解碼並且要將inJustDecodeBounds值設置爲false

01 public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
02         int reqWidth, int reqHeight) {
03  
04     // First decode with inJustDecodeBounds=true to check dimensions
05     final BitmapFactory.Options options = new BitmapFactory.Options();
06     options.inJustDecodeBounds = true;
07     BitmapFactory.decodeResource(res, resId, options);
08  
09     // Calculate inSampleSize
10     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
11  
12     // Decode bitmap with inSampleSize set
13     options.inJustDecodeBounds = false;
14     return BitmapFactory.decodeResource(res, resId, options);
15 }
        這個方法使得加載任意大小的Bitmap到展示100*100px縮略圖的ImageView中更加簡單,如下代碼所示: 
1 mImageView.setImageBitmap(
2     decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100100));

        你可以根據需要,按照類似的解碼過程,採用適當的BitmapFactory.decode*方法從其他資源中解碼Bitmap。 



via http://my.oschina.net/ryanhoo/blog/88242

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