前言
-
Glide
,該功能非常強大Android
圖片載入開源框架 相信大家並不陌生 -
正由於他的功能強大,所以它的原始碼非常複雜,這導致很多人望而卻步
-
本人嘗試將
Glide
的功能進行分解,並單獨針對每個功能進行原始碼分析,從而降低Glide
原始碼的複雜度。
接下來,我將推出一系列關於
Glide
的功能原始碼分析,有興趣可以繼續關注
- 今天,我將主要講解在使用
Glide
快取功能時的問題:為什麼Glide 的快取無起作用,希望你們會喜歡。
請先閱讀文章:
1. 背景
Glide
實現記憶體 & 磁碟快取是根據 圖片的快取Key
進行唯一標識- 開發者為了降低成本 & 安全,往往會將圖片存放在雲伺服器上
如 七牛雲 等等。
- 為了保護 客戶的圖片資源,圖片雲伺服器 會在圖片
Url
地址的基礎上再加一個token引數
http://url.com/image.jpg?token=a6cvva6b02c670b0a
複製程式碼
Glide
載入該圖片時,會使用加了token
引數的圖片Url
地址 作為id
引數,從而生成 快取Key
2. 問題
- 作為身份認證的
token
引數可能會發生變化,並不是一成不變 - 若
token
引數變了,則圖片Url
跟著變,則生成快取key的所需id引數發生變化,即 快取Key也會跟著變化 - 這導致同一張圖片,但因為
token
引數變化,而導致快取Key發生變化,從而使得Glide
的快取功能失效
快取Key發生變化,即同一個圖片的當前快取key 和 之前寫入快取的key不相同,這意味著 在讀取快取時 無法根據當前快取key 找到之前的快取,從而使得失效
3. 解決方案
3.1 原理
在 生成快取Key
的id引數 前,將 帶有token
引數的圖片Url
地址 去掉 token
引數,從而根據 初始的圖片Url
地址 生成快取Key
的id引數
實現了一個圖片的快取
Key
的id引數始終唯一 ,即等於 圖片Url
地址
3.2 儲備知識:生成快取Key的id引數的邏輯
生成快取Key
的id
引數的邏輯為:直接將圖片的 URL
地址作為快取Key的id
引數
請回看文章生成快取
Key
的程式碼:Android原始碼分析:手把手帶你深入瞭解Glide的快取機制
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
// 獲得了一個id字串,即需載入圖片的唯一標識
// 如,若圖片的來源是網路,那麼該id = 這張圖片的url地址
// fetcher = HttpUrlFetcher的例項,即呼叫HttpUrlFetcher.getid()->>分析19
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),transcoder, loadProvider.getSourceEncoder());
// 將該id 和 signature、width、height等10個引數一起傳入到快取Key的工廠方法裡,最終建立出一個EngineKey物件
// 建立原理:通過重寫equals() 和 hashCode(),保證只有傳入EngineKey的所有引數都相同情況下才認為是同一個EngineKey物件
// 該EngineKey 即Glide中的快取Key
...
}
<-- 分析19:getId() -->
public class HttpUrlFetcher implements DataFetcher<InputStream> {
...
private final GlideUrl glideUrl;
// GlideUrl = 在上篇文章講解 圖片載入 第2步load()中傳入圖片url地址時,Glide在內部把圖片url地址包裝成一個GlideUrl物件
@Override
public String getId() {
return glideUrl.getCacheKey();
// ->>分析20
}
<-- 分析20:getCacheKey() -->
public class GlideUrl {
private final URL url;
private final String stringUrl;
...
// GlideUrl建構函式
public GlideUrl(URL url) {
this(url, Headers.DEFAULT);
}
public GlideUrl(String url) {
this(url, Headers.DEFAULT);
}
public String getCacheKey() {
return stringUrl != null ? stringUrl : url.toString();
// 在生成GlideUrl物件時:
// 若傳入的是URL字串(即圖片地址),就直接返回該字串(大多數是這種情況)
// 若傳入的是URL物件,那麼就返回這個物件toString()後的結果。
}
...
}
複製程式碼
3.3 實現方案
即 我們只需重寫getCacheKey()
& 將 帶有token引數的圖片Url
地址 去掉 token引數 即可。
/**
* 程式碼實現:建立一個GlideUrl類的子類 & 重寫getCacheKey()
**/
// 1. 繼承GlideUrl
public class mGlideUrl extends GlideUrl {
private String mUrl;
// 建構函式裡 傳入 帶有token引數的圖片Url地址
public MyGlideUrl(String url) {
super(url);
mUrl = url;
}
// 2. 重寫getCacheKey()
@Override
public String getCacheKey() {
return mUrl.replace(deleteToken(), "");
// 通過 deleteToken() 從 帶有token引數的圖片Url地址中 去掉 token引數
// 最終返回一個沒有token引數、初始的圖片URL地址
// ->>分析1
}
// 分析1:deleteToken()
private String deleteToken() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
/**
* 使用快取時:需要在load()中傳入自定義的 mGlideUrl物件
**/
Glide.with(this)
.load(new mGlideUrl(url))
.into(imageView);
// 注:a. 若像之前直接傳入圖片的url地址,那麼在內部還是會使用原始的GlideUrl類
// b. 即直接將傳入傳入圖片的url地址作為快取key的Id引數,而沒有對token引數作任何處理
複製程式碼
4. 總結
- 本文主要對Glide的圖片快取功能 的使用問題進行講解
- 關於Glide的相關文章閱讀
- 下面我將繼續對
Glide
的其他功能進行原始碼分析 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記