高效使用Bitmaps有什麼好處? java
咱們經常提到的「Android程序優化」,一般指的是性能和內存的優化,即:更快的響應速度,更低的內存佔用。Android程序的性能和內存問題,大部分都和圖片緊密相關,而圖片的加載在不少狀況下很用到Bitmap(位圖)這個類。而因爲Bitmap自身的特性(將每一個像素的屬性所有保存在內存中),致使稍有不慎就會建立出一個佔用內存很是大的Bitmap對象,從而致使加載過慢,還會有內存溢出的風險。因此,Android程序要作優化,Bitmap的優化是必不可少的一步。 android
須要對Bitmap進行優化的情形 性能
首先請看一行代碼: 優化
mImageView.setImageResource(R.drawable.my_image);
這是一行從資源文件中加載圖片到ImageView的代碼。一般這段代碼沒什麼問題,但有些狀況下,你須要對這段代碼進行優化。例如當圖片的尺寸遠遠大於ImageView的尺寸時,或者當你要在一個ListView或GridView中批量加載一些大小未知的圖片時。實際上,以上這行代碼會在運行時使用BitmapFactory.decodeStream()方法將資源圖片生成一個Bitmap,而後由這個Bitmap生成一個Drawable,最後再將這個Drawable設置到ImageView。因爲在過程當中生成了Bitmap,所以若是你使用的圖片過大,就會致使性能和內存佔用的問題。另外,須要優化的情形不止這一種,這裏就再也不列舉。 spa
下面分步說明使用代碼來減少Bitmap的尺寸從而達到減少內存佔用的方法: code
1. 獲取原圖片尺寸 orm
一般,咱們使用BitmapFactory.decodeResource()方法來從資源文件中讀取一張圖片並生成一個Bitmap。但若是使用一個BitmapFactory.Options對象,並把該對象的inJustDecodeBounds屬性設置爲true,decodeResource()方法就不會生成Bitmap對象,而僅僅是讀取該圖片的尺寸和類型信息: 對象
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;
2. 根據原圖尺寸和目標區域的尺寸計算出合適的Bitmap尺寸 圖片
BitmapFactory.Options類有一個參數inSampleSize,該參數爲int型,他的值指示了在解析圖片爲Bitmap時在長寬兩個方向上像素縮小的倍數。inSampleSize的默認值和最小值爲1(當小於1時,解碼器將該值當作1來處理),且在大於1時,該值只能爲2的冪(當不爲2的冪時,解碼器會取與該值最接近的2的冪)。例如,當inSampleSize爲2時,一個2000*1000的圖片,將被縮小爲1000*500,相應地,它的像素數和內存佔用都被縮小爲了原來的1/4: 內存
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // 原始圖片的寬高 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; // 在保證解析出的bitmap寬高分別大於目標尺寸寬高的前提下,取可能的inSampleSize的最大值 while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
3. 根據計算出的inSampleSize生成Bitmap
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // 首先設置 inJustDecodeBounds=true 來獲取圖片尺寸 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // 計算 inSampleSize 的值 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 根據計算出的 inSampleSize 來解碼圖片生成Bitmap options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
這裏有一點要注意,就是要在第二遍decode以前把inJustDecodeBounds設置回false。
4. 調用以上的decodeSampledBitmapFromResource方法,使用自定尺寸的Bitmap。
若是你要將一張大圖設置爲一個100*100的縮略圖,執行如下代碼:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
到此,使用decodeResource()方法將一個大圖解析爲小尺寸bitmap的應用就完成了。同理,還可使用decodeStream(),decodeFile()等方法作相同的事,原理是同樣的。
延伸:一個Bitmap到底佔用多大內存?系統給每一個應用程序分配多大內存?
· Bitmap佔用的內存爲:像素總數 * 每一個像素佔用的內存。在Android中,Bitmap有四種像素類型:ARGB_888八、ARGB_444四、ARGB_56五、ALPHA_8,他們每一個像素佔用的字節數分別爲四、二、二、1。所以,一個2000*1000的ARGB_8888類型的Bitmap佔用的內存爲2000*1000*4=8000000B=8MB。
· Android根據設備屏幕尺寸和dpi的不一樣,給系統分配的單應用程序內存大小也不一樣,具體以下表(表格取自Android 4.4 Compatibility Definition Document (CDD)):
屏幕尺寸 | DPI | 應用內存 |
small / normal / large | ldpi / mdpi | 16MB |
small / normal / large | tvdpi / hdpi | 32MB |
small / normal / large | xhdpi | 64MB |
small / normal / large | 400dpi | 96MB |
small / normal / large | xxhdpi | 128MB |
xlarge | mdpi | 32MB |
xlarge | tvdpi / hdpi | 64MB |
xlarge | xhdpi | 128MB |
xlarge | 400dpi | 192MB |
xlarge | xxhdpi | 256MB |