對Glide的載入圖片流程不瞭解的可以看看之前寫的Glide範例原始碼分析
這篇主要介紹Glide在網路獲取流之後解析圖片,這裡講靜態圖片。
我們一般在Android中解析一張圖片一般是這樣的:
public static Bitmap decodeBitmap(byte[] source,int reqesutWidth,int requestHeight){
Bitmap resultBitmap = null;
BitmapFactory.Options option = new BitmapFactory.Options();
//設定option.inJustDecodeBounds 為true獲取圖片寬高
option.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(source,0,source.length,option);
//通過BitmapFactory.Options.inSampleSize的值儲存壓縮比例值
option.inSampleSize = caulateInSampleSize(option,reqesutWidth,requestHeight);
option.inJustDecodeBounds = false;
resultBitmap = BitmapFactory.decodeByteArray(source,0,source.length,option);
return resultBitmap;
}
/**
* 計算壓縮比例
* 通過BitmapFactory.Options.inSampleSize的值儲存該值
* */
private static int caulateInSampleSize(BitmapFactory.Options option, int reqesutWidth, int requestHeight) {
//inSampleSize為1即圖片原本大小 如:4 即寬高比為原來1/4
int inSampleSize = 1;
//原圖片寬高
int width = option.outWidth;
int height = option.outHeight;
//原圖片寬高其中一個大於要求的寬高才壓縮比例
if(height > requestHeight || width > reqesutWidth){
inSampleSize = Math.min(Math.round((float)height / requestHeight), Math.round((float)width / reqesutWidth));
}
return inSampleSize;
}
複製程式碼
BitmapFactory.decodeByteArray(source,0,source.length,option);
這句話可以換成BitmapFactory.decodeSteam()或BitmapFactory.decodeResource()
這裡拋磚引玉:其實Glide的核心程式碼也是這樣解析的,沒有什麼特別之處;只不過逼格高一點;
Glide的decode是Downsampler類
這個截圖是上篇文章的,該類是一個抽象類:
是這裡抽象:
該方法的作用就是上面丟擲程式碼計算BitmapFactory.Options.inSampleSize的;
這樣說吧:其實就是通過該欄位進行縮放;Glide也是在Downsampler類上實現了三種getSampleSize()該方法:
其中AT_LEAST的計算就是和上面丟擲的程式碼一毛一樣:
另外兩個:
BitmapFactory.inSampleSize是什麼
這裡要說一下inSampleSize幹什麼的,看官方解釋:
/**
* If set to a value > 1, requests the decoder to subsample the original
* image, returning a smaller image to save memory. The sample size is
* the number of pixels in either dimension that correspond to a single
* pixel in the decoded bitmap. For example, inSampleSize == 4 returns
* an image that is 1/4 the width/height of the original, and 1/16 the
* number of pixels. Any value <= 1 is treated the same as 1. Note: the
* decoder uses a final value based on powers of 2, any other value will
* be rounded down to the nearest power of 2.
*/
public int inSampleSize;
簡單理解就是該值越大,解析出來的Bitmap就越小。該值最小為1,取值為2的冪次。
下面是通過google翻譯得出:
如果設定為大於1的值,則請求解碼器對原始樣本進行二次取樣
影象,返回一個較小的影象,以節省記憶體。 樣本大小是
任一維度中對應於單個畫素的畫素數量
畫素在解碼的點陣圖中。 例如,inSampleSize == 4返回
原始寬度/高度的1/4,1/16的影象
畫素數。 任何值<= 1的處理都與1相同。注意:
解碼器使用基於2的冪的最終值,任何其他值將會
向下舍入到最接近的2的冪。
知道inSampleSize後,再回Downsampler類的decode()方法
Downsampler類decode()方法:
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {}
該方法返回一個Bitmap即解析流後的點陣圖。
- InputStream is 網路返回的流
- BitmapPool pool 用於解析Bitmap的快取
- int outWidth ImageView要求的寬
- int outHeight ImageView要求的高
- DecodeFormat decodeFormat 要求解析的格式(預設為RGB_565)
RGB565為一個畫素佔2個位元組,而ARGB8888為一個畫素佔4個位元組;所以用ARGB_8888解析出來的圖片會佔記憶體。
decode()方法前期準備
decode()方法開始會 建立兩個64k大小的位元組陣列;
獲取BitmapFactory.Options物件,該物件通過一個佇列管理,儘量減少建立新物件:
RecyclableBufferedInputStream 建立一個緩衝區,用於解析圖片頭部判別圖片方向
ExceptionCatchingInputStream 用於在讀取流中檢索異常(感覺挺牛B的,可以學習學習)
MarkEnforcingInputStream最終通過此流解析圖片
解析圖片的方向
這裡在流中設定一個標記位,目的就是通過最小讀取流來判斷圖片的方向。標記位最大值為5M
設定臨時儲存bitmap的容量;獲取圖片寬高;獲取圖片旋轉的角度
- options.inTempStorage該值是用於臨時儲存Bitmap,官網建議是16K
- getDimensions(invalidatingStream, bufferedStream, options);
該方法其實最開始已經有程式碼寫了,就是獲取寬高
decodeStream
該方法後面真正解析圖片還會呼叫一次,這裡呼叫就是為了獲取寬高:
這裡因為不是解析bitmap,所以result為null,目的之前說了就是為了獲取寬高,所以也設定一個標記位減少讀取時間。
- final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
該方法就是之前說的通過計算寬高比獲取sampleSize,最開頭說了,Glide預設用AT_LEAST
核心方法downsampleWithSize 解析圖片
這裡直接上原始碼:
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
//在KitKat之前,inBitmap大小必須與我們正在解碼的點陣圖大小完全匹配
Bitmap.Config config = getConfig(is, decodeFormat);
//sampleSize,該值為1 即圖片原大小
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
//當版本高於KITKAT 時候使用 BitmapPool
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
//在寫入BitmapFactory之前,BitmapFactory會清除Bitmap,所以getDirty是安全的
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
複製程式碼
Android4.1版本使用:ARGB8888; 如果圖片有透明度使用:ARGB8888;
當inSampleSize為1或大於Android4.4版本,並且屬於:
JPEG,PNG_A,PNG型別使用BitmapPool和BitmapFactory.Options.inBitmap
當然如果你的版本是4.4但是inSampleSize為1是那三種圖片型別還是可以使用上面說的兩個做快取;
最後其實就是一句:
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
所以萬變不離其中;當然Glide團隊使用了各種快取,優化解析工作。
最後總結一下:
- 其實解析圖片還是 final Bitmap result = BitmapFactory.decodeStream(is, null, options);
- 通過ExceptionCatchingInputStream此包裝類可以捕獲在流讀取過程中的異常,值的學習。
- 通過集合管理物件,儘量減少物件的建立,畢竟物件的建立對記憶體有一定開銷。
最尾說一下:BitmapPool這個類