問題
使用Glide加載圖片Glide.with(context).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageVIew)
注意使用了DiskCacheStrategy.ALL
當(dāng)展示的圖片是GIF的時(shí)候,會(huì)展示很慢疯坤,即使是本地有的圖片浸策,也經(jīng)常會(huì)顯示點(diǎn)位圖較長(zhǎng)時(shí)間才能展示出GIF。
還有一個(gè)條件司倚,就是gif要展示的大小與圖片的大小不一致,需要Glide進(jìn)行縮放。
重現(xiàn)問題方法:加載一個(gè)gif寝殴,使用緩存結(jié)果的磁盤緩存策略,就會(huì)很慢明垢,如果已經(jīng)成功加載過圖片蚣常,可以將緩存的結(jié)果刪除一下嘗試再次重現(xiàn)。
解決
- 加載的時(shí)候如果可以區(qū)分是gif或者是普通圖片袖外,可以將GIF加載設(shè)置
diskCacheStrategy
為Source
或者None
史隆。 - 實(shí)際上經(jīng)常加載網(wǎng)絡(luò)的圖片,是通過CDN下載的曼验,只有一個(gè)圖片的Key泌射,在下載完成之前并不知道這是一個(gè)普通圖片還是GIF,所以想如果能將GIF的圖片全部都使用
diskCacheStrategy
為Source
或者None
是不可行的(這樣會(huì)影響普通圖片的展示)鬓照。我們可以hook住GIF在encode的這一步熔酷,直接返回false,就跳過了緩存豺裆。Glide
在構(gòu)造完Glide
對(duì)象前后都會(huì)向我們回調(diào)拒秘,我們?cè)跇?gòu)造完成glide
對(duì)象之后替換掉他的encoder對(duì)象
号显,encode方法
直接返回false
。下面分析原因躺酒,先把解決代碼列出來押蚤。
@Override
public void registerComponents(Context context, Glide glide) {
ResourceEncoder gifEncoder = new ResourceEncoder() {
@Override
public boolean encode(Object data, OutputStream os) {
return false;
}
@Override
public String getId() {
return "";
}
};
try {
DataLoadProviderRegistry dataLoadProviderRegistry =
(DataLoadProviderRegistry) ReflectionUtil.getValue(glide, "dataLoadProviderRegistry");
if (dataLoadProviderRegistry == null) {
return;
}
DataLoadProvider<ImageVideoWrapper, GifBitmapWrapper> dataLoadProvider =
dataLoadProviderRegistry.get(ImageVideoWrapper.class, GifBitmapWrapper.class);
if (dataLoadProvider instanceof ImageVideoGifDrawableLoadProvider) {
ResourceEncoder<GifBitmapWrapper> encoder = dataLoadProvider.getEncoder();
if (encoder instanceof GifBitmapWrapperResourceEncoder) {
ReflectionUtil.setValue(encoder, "gifEncoder", gifEncoder);
}
}
} catch (Exception e) {
Log.e("ImageModule", "set gif encoder fail" + e);
}
}
原因
查看源碼發(fā)現(xiàn)glide會(huì)對(duì)gif的每一幀變換的結(jié)果進(jìn)行緩存,重新組成GIF保存下來羹应,實(shí)驗(yàn)的是一個(gè)2M揽碘,124幀的GIF,圖片被放大了3倍园匹,Glide變換后的GIF有54M雳刺。
因?yàn)閱栴}出在了encode的時(shí)候,所以只分析這一部分
在DecodeJob
中裸违,拿到源圖片之后會(huì)進(jìn)行transform
掖桦,然后將結(jié)果緩存起來
可以看到如果我們不設(shè)置緩存result的話就不會(huì)存儲(chǔ),就不會(huì)有問題供汛,將encoder和轉(zhuǎn)換之后的resource都傳給了writer
在
Writer
的write
方法就是將圖片寫到DiskLRUCache
中此時(shí)的encoder是
GifBitmapWrapperResourceEncoder
對(duì)象,看它的encode方法怔昨,第一次decode GIF的時(shí)候會(huì)走else分支上面的
gifEncoder
是GifResourceEncoder
的對(duì)象料饥,可以看到重新解析了GIF,實(shí)際上之前已經(jīng)解析過一遍了)朱监,對(duì)每一幀都要進(jìn)行變換岸啡,encoder
的addFrame
對(duì)第一幀進(jìn)行了存儲(chǔ)。
還好Glide有bitmap池赫编,內(nèi)存不會(huì)抖動(dòng)巡蘸,不過想想如果圖片有個(gè)100多幀甚至幾百幀,這個(gè)過程肯定是耗時(shí)的擂送,而且我們從前面的transform開始到encode都是同步調(diào)用悦荒,不存儲(chǔ)完成是無法走下去繼續(xù)展示的。如果不對(duì)圖片進(jìn)行縮放的話不用走這個(gè)encode嘹吨,也就不會(huì)慢搬味。
上面的代碼我們也可以看到最終使用了gifEncoder
這個(gè)成員,我們只要將GifBitmapWrapperResourceEncoder
這個(gè)成員換掉就可以了蟀拷。
而這個(gè)encoder是構(gòu)造glide的時(shí)候就已經(jīng)設(shè)置進(jìn)去了碰纬,并且不會(huì)再更改,所以是我們理想的hook點(diǎn)
注意
使用glide3.7
因?yàn)槭褂昧朔瓷湮史遥獙?duì)反射的類進(jìn)行keep