學習在保持用戶界面(UI)組件響應和避免超過應用內存限制的情況下,使用普通技術處理圖片的加載的方法。如果你不注意,圖片會快速的消息應用程序與分配的有效內存致使程序因爲“java.lang.OutofMemoryError: bitmap size excends VM budget”異常而崩潰。
這裏有幾個在應用程序中加載圖片時要注意的原因:
移動設備通常有系統資源限制。android設備上簡單應用一般分配16M的有效內存。安卓兼容性文檔 3.7版本。虛擬機的兼容性爲各種屏幕尺寸和密度提供了所需的最小應用程序存儲器。應用程序因該在最低內存限制下進行最優的操作。然而現在很多設備已經有了更高的配置。
Bitmaps佔用大量內存,尤其是豐富的圖片如照片。例如,在Galaxy Nexus的攝像頭拍到2592x1936像素(5像素)。如果用位圖配置argb_8888(默認從Android 2.3開始)然後加載這個圖像到內存大約需要19mb內存(2592×1936×4字節),馬上就耗盡了每個應用程序的限制,一些設備。
android應用程序經常需要一次性加載多個圖片。例如:ListView , GridView, ViewPager通常需要一次在屏幕上加載多個圖片。同時準備隨手指滑動還沒有顯示的更多的圖片。
圖片用各種形狀和大小,圖片有豐富的信息如照片。在系統的圖庫應用中展示的圖片,他們的手機照相機的分辨率要遠遠高於手機屏幕的分辨率。
由於有限的使用內存理想情況下,我們只需要在內存中加載一個較低分辨率的版本的圖片。最低本的圖片應該和顯示圖片的的組件的大小相匹配。在這種情況下使用高分辨率的圖片沒有什麼明顯的好處。由於要進行縮放,會佔用寶貴的內存,帶來額外的性能開銷。
讀取不同大小和類型的圖片
BitmapFactory類提供幾種不同的編碼方法(decodeByteArray(), decodeResourece(), decodeResource(),…)從不同類型資源中創建一個圖片。根據不同圖片資源選擇不同的編碼方法。這些方法試圖爲構建位圖分配內存,因此可以很容易地導致OutOfMemory異常。每個方法都添加參數讓你通過配置BitmapFactory.Options類。設置Options的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;
爲了避免java.lang.OutOfmemory異常,在編碼圖片前請檢查圖片的大小。除非你確定提供的圖片資源大小適合放到可用內存中。
按比例縮小的圖片加載到內存中
在圖片大小已知的情況下,他們可以直接完整的加載到內存中或者加載一個按比例縮小的圖片到內存中。這裏有幾個參考因素:
- 估計加載完整原圖使用的內存。
- 估計你加載圖片的內存和應用程序中其他需要內存的的內存總和。
- 計算將要加載圖片的ImageView或者其他UI組件的大小。
- 當前設備的分辨率和尺寸
例如,不值得爲1024x768像素圖像加載到內存如果它最終將在ImageView中一128x96像素縮略圖顯示。
通過設BitmapFactory.Options.insampleSize爲true,告訴編碼器得到一個少用內存的縮放圖。例如,一個圖片的尺寸爲2048X1536,在編碼時設置insamplesize = 4時得到圖片大小是512x384.加載到內存中需要0.75M而不用12M內存。這裏有一個計算縮放比例的方法:
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;
}
使用下面的比例縮放圖片的方法,首先設置inJustDecodeBounds設置爲true,利用上邊的方法獲取縮放比例並將值賦值給Options.inSampleSize,同時這設置inJustDecodeBounds = false.
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);
}
這個方法可以很容易的加載任意尺寸的圖片到ImageView中。例如下面的方法:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));