DataSource(數(shù)據(jù)源)
數(shù)據(jù)源有5種類型:
- LOCAL:本地?cái)?shù)據(jù)盒延,例如本地圖片文件,也可能是通過ContentProvider共享的遠(yuǎn)程數(shù)據(jù),比如在ContentProvider中進(jìn)行網(wǎng)絡(luò)請(qǐng)求。
- REMOTE:遠(yuǎn)程數(shù)據(jù),例如通過網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)刺覆。
- DATA_DISK_CACHE:緩存在磁盤的原始數(shù)據(jù)。
- RESOURCE_DISK_CACHE:緩存在磁盤的解碼史煎、變換后的數(shù)據(jù)谦屑。
- MEMORY_CACHE:存儲(chǔ)在內(nèi)存中的數(shù)據(jù)。
DiskCacheStrategy(磁盤緩存策略)
我們知道篇梭,在使用Glide進(jìn)行請(qǐng)求時(shí)氢橙,可以設(shè)置磁盤緩存策略。之后在DataFetcherGenerator請(qǐng)求數(shù)據(jù)成功后恬偷,會(huì)根據(jù)磁盤緩存策略DiskCacheStrategy和LoadData的Fetcher對(duì)應(yīng)的DataSource悍手,來決定是否緩存數(shù)據(jù)。
主要有:
- ALL(原始數(shù)據(jù)和處理后的數(shù)據(jù)都緩存)
- NONE(都不緩存)
- DATA(只緩存原始數(shù)據(jù))
- RESOURCE(只緩存處理后的數(shù)據(jù)袍患,即經(jīng)過縮放等轉(zhuǎn)換后的數(shù)據(jù))
- AUTOMATIC(它會(huì)嘗試對(duì)本地和遠(yuǎn)程圖片使用最佳的策略坦康。
當(dāng)你加載遠(yuǎn)程數(shù)據(jù)(比如,從URL下載)時(shí)诡延,AUTOMATIC 策略僅會(huì)存儲(chǔ)未被你的加載過程修改過(比如滞欠,變換,裁剪–譯者注)的原始數(shù)據(jù)肆良,因?yàn)橄螺d遠(yuǎn)程數(shù)據(jù)相比調(diào)整磁盤上已經(jīng)存在的數(shù)據(jù)要昂貴得多筛璧。對(duì)于本地?cái)?shù)據(jù)逸绎,AUTOMATIC 策略則會(huì)僅存儲(chǔ)變換過的縮略圖,因?yàn)榧词鼓阈枰俅紊闪硪粋€(gè)尺寸或類型的圖片夭谤,取回原始數(shù)據(jù)也很容易棺牧。默認(rèn)使用這種磁盤緩存策略)
// DiskCacheStrategy.java
public abstract class DiskCacheStrategy {
// 原始數(shù)據(jù)是否可以緩存
public abstract boolean isDataCacheable(DataSource dataSource);
// 解碼(變換)后的數(shù)據(jù)是否可以緩存
public abstract boolean isResourceCacheable(
boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy);
// 是否可以解碼之前緩存的解碼(變換)后的數(shù)據(jù)
public abstract boolean decodeCachedResource();
// 是否可以解碼之前緩存的原始數(shù)據(jù)
public abstract boolean decodeCachedData();
public static final DiskCacheStrategy ALL =
new DiskCacheStrategy() {
// 如果是遠(yuǎn)程數(shù)據(jù),則可以緩存成原始數(shù)據(jù)
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
// 如果是本地?cái)?shù)據(jù)朗儒、遠(yuǎn)程數(shù)據(jù)和緩存在磁盤中的原始數(shù)據(jù)颊乘,則可以緩存成解碼、變換后的數(shù)據(jù)RESOURCE_DISK_CACHE
// 因?yàn)榫彺嬖诖疟P中的解碼醉锄、變換后的數(shù)據(jù)RESOURCE_DISK_CACHE沒有必要再次緩存疲牵,
// 而內(nèi)存中如果有數(shù)據(jù)的話,說明之前已經(jīng)通過其他4種方式加載過資源了榆鼠,如果可以緩存的話已經(jīng)緩存了,也就沒必要緩存內(nèi)存數(shù)據(jù)了
@Override
public boolean isResourceCacheable(
boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
return dataSource != DataSource.RESOURCE_DISK_CACHE
&& dataSource != DataSource.MEMORY_CACHE;
}
// 可以解碼緩存在磁盤中的解碼亥鸠、變換后的數(shù)據(jù)
@Override
public boolean decodeCachedResource() {
return true;
}
// 可以解碼緩存在磁盤中的原始數(shù)據(jù)
@Override
public boolean decodeCachedData() {
return true;
}
};
public static final DiskCacheStrategy AUTOMATIC =
new DiskCacheStrategy() {
// 如果是遠(yuǎn)程數(shù)據(jù)妆够,則可以緩存成原始數(shù)據(jù)
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
// 如果是緩存在磁盤的原始數(shù)據(jù)或 需要transform的本地?cái)?shù)據(jù),則可以緩存成解碼负蚊、變換后的數(shù)據(jù)
@Override
public boolean isResourceCacheable(
boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
// 可以解碼緩存在磁盤中的解碼神妹、變換后的數(shù)據(jù)
@Override
public boolean decodeCachedResource() {
return true;
}
// 可以解碼緩存在磁盤中的原始數(shù)據(jù)
@Override
public boolean decodeCachedData() {
return true;
}
};
}
BitmapPool
Android 2.3.3(API 級(jí)別 10)及以下的版本:Bitmap內(nèi)存在Native層,使用Bitmap對(duì)象的recycle方法回收內(nèi)存家妆,無法復(fù)用鸵荠。
從Android3.0開始到8.0以前,bitmap對(duì)象存儲(chǔ)在java堆內(nèi)存中伤极,java虛擬機(jī)會(huì)自動(dòng)回收內(nèi)存蛹找。
在8.0以后Bitmap內(nèi)存在Native層,所以我們要手動(dòng)釋放bitmap哨坪。
在 Android 4.4(API 級(jí)別 19)之前(3.0-4.3)復(fù)用bitmap的前提是:
- 被解碼的圖像必須是 JPEG 或 PNG 格式
- 被復(fù)用的圖像寬高必須等于 解碼后的圖像寬高
- 解碼圖像的 BitmapFactory.Options.inSampleSize 設(shè)置為 1 , 也就是不能縮放
- 被復(fù)用的圖像的像素格式 Config ( 如 RGB_565 ) 會(huì)覆蓋設(shè)置的 BitmapFactory.Options.inPreferredConfig 參數(shù)
在 4.4 之后只要 inBitmap 的大小比目標(biāo) Bitmap 大即可庸疾,且解碼圖像的BitmapFactory.Options.inSampleSize 可以大于1。
BitmapPool是一個(gè)Bitmap對(duì)象池当编,用于復(fù)用已有的未recycle的Bitmap對(duì)象届慈。
它主要有兩個(gè)子類:
- BitmapPoolAdapter,空實(shí)現(xiàn)
- LruBitmapPool忿偷,基于LruPoolStrategy策略來緩存對(duì)象金顿。
GlideBuilder在創(chuàng)建Glide時(shí),根據(jù)memorySizeCalculator.getBitmapPoolSize()
獲取設(shè)備推薦的bitmap緩存池大欣鹎拧(字節(jié)為單位)揍拆,如果大于0,則使用LruBitmapPool茶凳,否則使用BitmapPoolAdapter礁凡。
我們也可以自己實(shí)現(xiàn)BitmapPool高氮,設(shè)置給Glide。
MemoryCache(二級(jí)內(nèi)存緩存)
主要有兩個(gè)子類:
- MemoryCacheAdapter:空實(shí)現(xiàn)
- LruResourceCache:默認(rèn)實(shí)現(xiàn)顷牌,繼承自LruCache剪芍,實(shí)現(xiàn)了MemoryCache,內(nèi)部使用LinkedHashMap來實(shí)現(xiàn)LRU窟蓝。
LruResourceCache的key是一個(gè)自定義的Key類對(duì)象罪裹,維護(hù)了width、height运挫、url等信息状共。
LruResourceCache的value是一個(gè)Resource<?>包裝類,因?yàn)镚lide支持多種類型的結(jié)果谁帕,例如Bitmap峡继、Drawable等。
對(duì)于低內(nèi)存設(shè)備匈挖,默認(rèn)的緩存大小是應(yīng)用內(nèi)存(系統(tǒng)為每個(gè)應(yīng)用分配的近似內(nèi)存值)的33%碾牌,非低內(nèi)存設(shè)備則是40%。ps:這里的緩存包括:Resouce的緩存儡循、Bitmap對(duì)象池舶吗、數(shù)組對(duì)象池,這三種都采用了Lru算法進(jìn)行緩存择膝。
我們也可以自己實(shí)現(xiàn)MemoryCache類設(shè)置給Glide誓琼。
ActiveResources(一級(jí)內(nèi)存緩存)
正在被使用的資源會(huì)被放入ActiveResources。
// ActiveResources.java
final class ActiveResources {
// 是否允許正在使用的資源被保留(如果是的話肴捉,在ResourceWeakReference中會(huì)強(qiáng)引用該資源腹侣,release時(shí)需要手動(dòng)置空)
private final boolean isActiveResourceRetentionAllowed;
// 用于將resourceReferenceQueue中被回收的ResourceWeakReference從activeEngineResources中移除
private final Executor monitorClearedResourcesExecutor;
// 正在被使用的資源
@VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
// 當(dāng)ResourceWeakReference引用的資源被回收時(shí),ResourceWeakReference會(huì)被放入該隊(duì)列
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
// 資源釋放監(jiān)聽
private ResourceListener listener;
// 是否中斷齿穗,是的話筐带,monitorClearedResourcesExecutor將會(huì)停止工作
private volatile boolean isShutdown;
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
// 執(zhí)行清理工作,將resourceReferenceQueue中被回收的ResourceWeakReference從activeEngineResources中移除
cleanReferenceQueue();
}
});
}
// 將正在使用的資源包裝成ResourceWeakReference缤灵,并放入activeEngineResources
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
// 如果該key之前已經(jīng)有對(duì)應(yīng)的資源了伦籍,則清理舊資源
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
// 將key對(duì)應(yīng)的ResourceWeakReference從activeEngineResources中移除,并清理舊資源
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
// 將ResourceWeakReference從activeEngineResources中移除腮出,如果ResourceWeakReference強(qiáng)引用了資源帖鸦,則回調(diào)通知Engine放入MemoryCache中
void cleanupActiveReference(ResourceWeakReference ref) {
synchronized (this) {
// 將key對(duì)應(yīng)的ResourceWeakReference從activeEngineResources中移除
activeEngineResources.remove(ref.key);
// 如果資源沒有被強(qiáng)引用則return
if (!ref.isCacheable || ref.resource == null) {
return;
}
}
// 如果資源被強(qiáng)引用了,則回調(diào)通知Engine放入MemoryCache中
EngineResource<?> newResource =
new EngineResource<>(
ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
// 執(zhí)行清理工作胚嘲,將resourceReferenceQueue中被回收的ResourceWeakReference從activeEngineResources中移除
void cleanReferenceQueue() {
while (!isShutdown) {
// 從隊(duì)列中獲取被回收的資源作儿,當(dāng)隊(duì)列沒數(shù)據(jù)時(shí)會(huì)阻塞在這里
ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
// 清理ResourceWeakReference及其引用資源
cleanupActiveReference(ref);
// ... 回調(diào)通知ResourceWeakReference從resourceReferenceQueue中移除了
}
}
// 關(guān)閉清理資源的線程池
void shutdown() {
isShutdown = true;
if (monitorClearedResourcesExecutor instanceof ExecutorService) {
ExecutorService service = (ExecutorService) monitorClearedResourcesExecutor;
Executors.shutdownAndAwaitTermination(service);
}
}
// 弱引用類,用于引用正在使用的資源馋劈,當(dāng)資源被回收時(shí)會(huì)被放入resourceReferenceQueue攻锰,之后會(huì)在線程池中將它從activeEngineResources中移除晾嘶,如果被引用的資源是可緩存在內(nèi)存中的,則會(huì)強(qiáng)引用資源娶吞,在被清理時(shí)垒迂,會(huì)回調(diào)到Engine中放入MemoryCache中
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
// 資源的key
final Key key;
// 資源是否可以緩存在內(nèi)存中
final boolean isCacheable;
// 被引用的資源(如果資源是可緩存在內(nèi)存中的,則不為null)
Resource<?> resource;
ResourceWeakReference(
@NonNull Key key,
@NonNull EngineResource<?> referent,
@NonNull ReferenceQueue<? super EngineResource<?>> queue,
boolean isActiveResourceRetentionAllowed) {
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
// 如果資源可以緩存在內(nèi)存中妒蛇,且允許持有正在使用的資源机断,則強(qiáng)引用資源
this.resource =
referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource())
: null;
isCacheable = referent.isMemoryCacheable();
}
// 清理資源
void reset() {
resource = null;
clear();
}
}
}
通過上面代碼的注釋,可以知道:
- 正在使用的資源會(huì)被轉(zhuǎn)成弱引用ResourceWeakReference绣夺,保存在ActiveResouces的activeEngineResources中吏奸。
- 當(dāng)資源被jvm回收時(shí),ResourceWeakReference會(huì)被放入隊(duì)列中陶耍。
- ActiveResources中有個(gè)線程池奋蔚,會(huì)循環(huán)從隊(duì)列中取被回收資源的ResourceWeakReference,將它從activeEngineResources中移除烈钞。如果ResourceWeakReference.resource不為null(條件:isActiveResourceRetentionAllowed為true泊碑,且該資源可以緩存在內(nèi)存中),則會(huì)回調(diào)到Engine中將資源加入MemoryCache棵磷。