采樣率壓縮
根據(jù)ImageView的大小,通過設置inSampleSize采樣率,加載壓縮后的圖片。如下:
/**
* Bitmap采樣率壓縮[推薦壓縮方案]
*
* @param resources
* @param resourcesId 圖片資源ID
* @param requestWidth 需要適配的寬度
* @param requestHeight 需要適配的高度
*/
public static Bitmap decodeBitmapFromResource(Resources resources,
int resourcesId, int requestWidth, int requestHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
//1, true 只會解析圖片的原始寬、高,不會真正的加載圖片。
options.inJustDecodeBounds = true;
//2, 獲取加載圖片顯示在ImageView上的采樣率(采樣lv會造成一定程度的失真)
options.inSampleSize = calculateInSampleSize(options,
requestWidth, requestHeight);
//3, false 可以獲取圖片的Bitmap
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(resources, resourcesId, options);
}
private static int calculateInSampleSize(BitmapFactory.Options options,
int requestWidth, int requestHeight) {
if (options == null) return 1;
//通過options.outWidth獲取的寬高和圖片所放目錄及屏幕密度有關。
final int originWidth = options.outWidth;
final int originHeight = options.outHeight;
int inSampleSize = 1;
boolean flag = (originWidth > requestWidth * inSampleSize)
&& (originHeight > requestHeight * inSampleSize);
do {
inSampleSize *= 2;
} while (flag);
return inSampleSize;
}
inSampleSize的值應為2的非負整數(shù)次冪(1,2,4,... ),否則會被系統(tǒng)向下取整并找到一個最接近的值。
Bitmap
Bitmap.compress(...):負責向輸入流中寫入一個針對透明度、圖像深度的壓縮版本Bitmap,即質(zhì)量壓縮。
/**
* 向指定的輸出流中寫入一個壓縮版本的位圖,主要針對透明度、圖像深度等進行壓縮。
*
* @param format 要壓縮圖片的格式。
* @param quality 表示圖片壓縮質(zhì)量,范圍0-100,0表示將圖片質(zhì)量壓縮成最小,
* 100表示將圖片質(zhì)量壓縮成最大(個人理解為不壓縮)。
* 本身是PNG格式的圖片會無視質(zhì)量壓縮,本身是Jpeg格式的圖片壓縮成PNG格式內(nèi)存會變大。
* @param stream 壓縮數(shù)據(jù)將要寫入的輸出流。
*
* 如果返回true,表示成功壓縮到對應的流中。
*/
@WorkerThread
public boolean compress(CompressFormat format,int quality,OutputStream stream)
質(zhì)量壓縮并不能改變圖片在內(nèi)存中的占用,只是改變輸出流在文件中的大小。一般用于上傳文件時將Btimap壓縮成流,降低上傳流量成本。
Bitmap.getAllocationByteCount()/getByteCount():獲取位圖所占內(nèi)存。
/**
* 獲取此Bitmap所占內(nèi)存。
* 在Android API19后,推薦用getAllocationByteCount(),表示獲得Bitmap已分配過的內(nèi)存(推薦理由:引用未置空,內(nèi)存不回收)。
* 如果該Bitmap 未 被重復用來解碼其他尺寸的Bitmap時,兩方法值相同。
*/
public final int getByteCount() {
if (mRecycled) {
return 0;
}
//getHeight()是圖片加載的高度(單位:pix)
return getRowBytes() * getHeight();
}
Bitmap內(nèi)存重用: 在Android API19 之前只能重用相同大小的Bitmap的內(nèi)存,而Android 4.4及以后版本則只要后來的Bitmap比之前的小即可。多個Bitmap可以復用一塊內(nèi)存,減少內(nèi)存開支,提高性能。
BitmapFactory
BitmapFactory類負責創(chuàng)建Bitmap,一般從files、streams或byte數(shù)組中獲取數(shù)據(jù)源。具體調(diào)用方法有decodeFile,decodeResource,decodeStream和decodeByteArray。
/**
* 配置圖片的色彩模式
* 如果不為空,解碼器在解析時會按此配置解析。
* 如果為空或無法滿足請求時,解碼器會根據(jù)屏幕色深選擇最佳配置。
* 如果原始圖片有透明度的話,也會按此配置設置。
*
* 可配置ALPHA_8,RGB_565,ARGB_4444,ARGB_8888(默認值)
*/
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
如果為空或無法滿足請求時,系統(tǒng)會自動選擇最佳色彩模式。
色彩模式:
ARGB分別表示Alpha(透明度)通道,R(red紅色)通道,G(Green綠色)通道,(Blue藍色)通道。
- ALPHA_8: ALPHA通道占用8位,即1個字節(jié)
- RGB_565: R通道占用5位,G通道占用6位,B通道占用5位,共16位即2個字節(jié)
- ARGB_4444: A,R,G,B四個通道各占用4位,共16位即2個字節(jié)
- ARGB_8888: A,R,G,B四個通道各占用8位,共32位即4個字節(jié)
根據(jù)色彩模式的命名即可知道各個通道所占位或字節(jié)(1Byte = 8bit)。
Bitmap占用內(nèi)存
Bitmap占用內(nèi)存= 圖片最終加載的分辨率 * 被動壓縮比^2 * 單位像素內(nèi)存。
影響B(tài)itmap占用內(nèi)存的因素:
- 圖片最終加載的分辨率;
- 圖片的格式(PNG/JPEG/BMP/WebP);
- 圖片所存放的drawable目錄;
- 圖片屬性設置的色彩模式;
- 設備的屏幕密度;
圖片最終加載的分辨率:即圖片經(jīng)過壓縮、裁剪等方式,最終加載到Bitmap中的分辨率。一般通過設置主動壓縮比,或在XML中設置scaleType屬性,或設置ImageView的寬高進行壓縮、裁剪。
主動壓縮比:
- 通過設置inSampleSize縮放值,設置圖片最終要加載的尺寸;
- Glide的View視圖值/原圖值的縮放比,是讓ImageView的顯示寬高參與主動縮放比計算。
被動壓縮比:設備屏幕密度/圖片所在drawable文件夾屏幕密度
- 被動縮放比只在當Bitmap文件位于drawable后面有dpi層級的文件中時生效。drawable后面沒有dpi層級文件夾dpi為160;
- 如果Bitmap位圖文件位于assets包這樣的外部文件或者是URL網(wǎng)絡地址(下載后以File格式存儲在SD卡或緩存),是有沒有被動縮放比的(即默認為1),不影響內(nèi)存結(jié)果。
PNG/JPEG/BMP/WebP格式的區(qū)別:
- JPEG格式的圖片不支持Alpha(透明度)屬性的。
- PNG是無損壓縮的,無法進行質(zhì)量壓縮。
- WebP格式的圖片比JPEG更節(jié)省空間,WebP默認只支持Android 4.0以上。