在Android開發中,圖片一直是應用中占據內存最大的一部分,大圖加載甚至會直接造成應用的崩潰,而我們日常所需要進行的性能優化圖片壓縮更是必不可少的一部分,所以合理的應用圖片壓縮就顯得尤為重要。
1. 認識圖片內存的計算
如果要壓縮一張圖片,我們首先要對一張圖片大小的構成有一個簡單的理解,這里有一張美女的圖片
我們在屬性中查看一下它的信息,這個美女的分辨率是1080*1920.那么它在我們的Android圖片中加載成bitmap是多大呢,我們打個log
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.c);
Log.d("TAG",bitmap.getAllocationByteCount()+"===");
結果是8294400。
它的由來是8294400= 1080×1920×4。
2. 為什么要乘以4?(了解圖片的基本屬性)
Android中圖片有四種屬性,分別是:
ALPHA_8:每個像素占用1byte內存
ARGB_4444:每個像素占用2byte內存
ARGB_8888:每個像素占用4byte內存 (默認)
RGB_565:每個像素占用2byte內存
A:透明度(Alpha)
R:紅色(Red)
G:綠(Green)
B:藍(Blue)
Android默認以ARGB_8888的方式加載圖片,所以一個像素點要占據4個字節的內存。
3. 這四個字節都讓誰給占了?
Bitmap.Config ARGB_8888:由4個8位組成,即A=8,R=8,G=8,B=8,那么一個像素點占8+8+8+8=32位(4字節)
Bitmap.Config ARGB_4444:由4個4位組成,即A=4,R=4,G=4,B=4,那么一個像素點占4+4+4+4=16位 (2字節)
Bitmap.Config RGB_565:沒有透明度,R=5,G=6,B=5,,那么一個像素點占5+6+5=16位(2字節)
Bitmap.Config ALPHA_8:每個像素占8位,只有透明度,沒有顏色
這樣明白了吧。
小提示
你如果要真實測試的時候這個資源圖片應該放在對應的drawable分辨率下,Android計算資源圖片加載內存的時候如果你已經把圖片放在了指定的分辨率文件中,它就會直接依據該分辨率加載該圖片。
雖然我們可以根據Android中圖片的屬性來壓縮圖片,但是今天我們要講的是根據采樣率來壓縮圖片,也就是我們平時所說的質量壓縮。
4.(正題)質量壓縮
質量壓縮也就是采樣率壓縮,它壓縮的本質就是本來一張圖片的寬有1080個像素,我們采樣率為2就是每兩個像素取一個來采樣,不采樣的就會被舍棄,所以說如果采樣率過高圖片就會模糊不清。
4.1 主體方法
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);
//通過指定分辨率來確定采樣率的大小
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false; //這個時候就要分配內存了
return BitmapFactory.decodeResource(res, resId, options);
}
calculateInSampleSize(options, reqWidth, reqHeight)中參數的寬高應當是根據我們實際開發需要來確定。
4.2 采樣
在這里解碼器對圖片進行下采樣,以將較小版本加載到內存中。
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;
}
例如,分辨率為 2048x1536 且以 4 作為 inSampleSize 進行解碼的圖片會生成大約 512x384 的位圖(說人話就是一張橫向有2048個像素,縱向有1536個像素的圖片每4個像素取一個所生成的圖片就是我們壓縮生成的512x384的圖片)。將此圖片加載到內存中需使用 0.75MB,而不是完整圖片所需的 12MB(假設位圖配置為 ARGB_8888)。
采用下面的方法,您可以輕松地將任意大尺寸的位圖加載到顯示 100x100 像素縮略圖的 ImageView 中,如以下示例代碼所示:
imageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
參考文檔:https://developer.android.com/topic/performance/graphics/load-bitmap