如果沒有緩存,在大量的網(wǎng)絡(luò)請求從遠(yuǎn)程獲取圖片時會造成網(wǎng)絡(luò)流量的浪費读恃,尤其是面對高清大圖的加載更是如此蕾域,為了節(jié)省帶寬醒叁,也為了減少用戶等待的時間,合理的緩存方式必不可少枢贿,這也是Glide圖片框架的強大之處瑰抵。另外Glide的緩存機(jī)制可以說是非常高頻的問題暇榴,Glide有幾級緩存橱赠?Glide讀取緩存的順序和時機(jī)是什么尤仍?Glide存放緩存的順序和時機(jī)又是什么?
1.Glide中緩存概念簡述
Glide中的緩存分為兩部分狭姨,內(nèi)存緩存和硬盤緩存宰啦。
1.1 內(nèi)存緩存
內(nèi)存緩存又分為兩級,一級是LruCache緩存送挑,一級是弱引用緩存绑莺。
內(nèi)存緩存的作用:防止應(yīng)用重復(fù)將圖片數(shù)據(jù)讀取到內(nèi)存當(dāng)中暖眼。
LruCache緩存:不在使用中的圖片使用LruCache來進(jìn)行緩存惕耕。
弱引用緩存:把正在使用中的圖片使用弱引用來進(jìn)行緩存
為什么設(shè)計弱引用緩存?
因為內(nèi)存緩存使用LRU算法诫肠,當(dāng)你使用Gilde加載并顯示第一張圖片時司澎,后面又加載了很多圖片,同時你的第一張圖片還在用栋豫。這個時候內(nèi)存緩存根據(jù)LRU算法可能會刪除你正在使用的第一張照片挤安。這樣的后果就是你正在使用的照片找不到,后果就是程序崩潰丧鸯。
1.2 硬盤緩存
硬盤緩存的作用:防止應(yīng)用重復(fù)從網(wǎng)絡(luò)或其他地方重復(fù)下載和讀取數(shù)據(jù);
2.緩存源碼流程
memory cache和disk cache在Glide創(chuàng)建的時候也被創(chuàng)建了蛤铜,并傳給了Engine引擎類,Glide創(chuàng)建的代碼在GlideBuilder.build(Context)方法丛肢。
@NonNull
Glide build(@NonNull Context context) {
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
...);
}
return new Glide(
...
memoryCache,
...);
}
1. 內(nèi)存緩存--memoryCache
通過代碼可以看到 memoryCache 被放入 Engine 和 Glide 實例中围肥。在Engine中利用memoryCache進(jìn)行存取操作,Glide 實例中的memoryCache是用來在內(nèi)存緊張的時候蜂怎,通知memoryCache釋放內(nèi)存穆刻。
Glide如何監(jiān)聽內(nèi)存緊張?
當(dāng)應(yīng)用內(nèi)存不足的時候杠步,會回調(diào)
ComponentCallbacks2
接口的void onTrimMemory(@TrimMemoryLevel int level);
或者ComponentCallbacks
接口的void onLowMemory();
;ComGlide實現(xiàn)了ComponentCallbacks2
接口(ComponentCallback2繼承了ComponentCallback)氢伟,在Glide創(chuàng)建完成后榜轿,通過```applicationContext.registerComponentCallbacks(glide)``將其注冊進(jìn)Application,因此Glide 實例可以監(jiān)聽內(nèi)存緊張的信號朵锣。
@Override
public void onLowMemory() {
clearMemory();
}
public void clearMemory() {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
// memory cache needs to be cleared before bitmap pool to clear re-pooled Bitmaps too. See #687.
memoryCache.clearMemory();
bitmapPool.clearMemory();
arrayPool.clearMemory();
}
// Glide
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}
public void trimMemory(int level) {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
for (RequestManager manager : managers) {
manager.onTrimMemory(level);
}
// memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
在onlowMemory 中Glide會將一些緩存的內(nèi)存進(jìn)行清除谬盐,方便進(jìn)行內(nèi)存回收,當(dāng)onTrimMemory被調(diào)用的時候诚些,如果level是系統(tǒng)資源緊張设褐,Glide會將Lru緩存和BitMap重用池相關(guān)的內(nèi)容進(jìn)行回收。如果是其他的原因調(diào)用onTrimMemory泣刹,Glide會將緩存的內(nèi)容減小到配置緩存最大內(nèi)容的1/2助析。
memoryCache是一個使用LRU(least recently used)算法實現(xiàn)的內(nèi)存緩存類LruResourceCache,繼承至LruCache類椅您,并實現(xiàn)了MemoryCache接口外冀。LruCache定義了LRU算法實現(xiàn)相關(guān)的操作,而MemoryCache定義的是內(nèi)存緩存相關(guān)的操作掀泳。
LruCache源碼分析
public class LruCache<K, V> {
// 數(shù)據(jù)最終存在 LinkedHashMap 中
private final LinkedHashMap<K, V> map;
...
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
// 創(chuàng)建一個LinkedHashMap雪隧,accessOrder 傳true
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
...
LruCache 構(gòu)造方法里創(chuàng)建一個LinkedHashMap,accessOrder 參數(shù)傳true员舵,表示按照訪問順序排序脑沿,數(shù)據(jù)存儲基于LinkedHashMap。
LinkedHashMap在HashMap的數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)上马僻,添加了雙向鏈表的數(shù)據(jù)結(jié)構(gòu)庄拇,再加上對 LinkedHashMap 的數(shù)據(jù)操作上鎖,就實現(xiàn)了LruCache的緩存策略韭邓,即最少最近訪問措近。
當(dāng)調(diào)用 put()方法時,就會在集合中添加元素女淑,并調(diào)用
trimToSize()判斷緩存是否已滿瞭郑,如果滿了就用 LinkedHashMap 的迭代器刪除隊尾元素,即近期最少訪問的元素鸭你。
當(dāng)調(diào)用 get()方法訪問緩存對象時屈张,就會調(diào)用 LinkedHashMap 的 get()方法獲得對應(yīng)集合元素,同時會更新該元素到隊頭袱巨。
2.磁盤緩存
diskCacheFactory是創(chuàng)建DiskCache的Factory阁谆,DiskCache接口定義。
DiskCache.Factory的默認(rèn)實現(xiàn)為InternalCacheDiskCacheFactory
瓣窄。在InternalCacheDiskCacheFactory
中會默認(rèn)會創(chuàng)建一個250M的緩存目錄笛厦,其路徑
/data/data/{package}/cache/image_manager_disk_cache/。
繼續(xù)看其父類DiskLruCacheFactory的代碼:
public class DiskLruCacheFactory implements DiskCache.Factory {
// 省略部分代碼
@Override
public DiskCache build() {
File cacheDir = cacheDirectoryGetter.getCacheDirectory();
if (cacheDir == null) {
return null;
}
if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) {
return null;
}
return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
}
}
DiskLruCacheFactory.build()方法會返回一個DiskLruCacheWrapper類的實例俺夕,看下DiskLruCacheWrapper的實現(xiàn)裳凸。
public class DiskLruCacheWrapper implements DiskCache {
private static final String TAG = "DiskLruCacheWrapper";
private static final int APP_VERSION = 1;
private static final int VALUE_COUNT = 1;
private static DiskLruCacheWrapper wrapper;
private final SafeKeyGenerator safeKeyGenerator;
private final File directory;
private final long maxSize;
private final DiskCacheWriteLocker writeLocker = new DiskCacheWriteLocker();
private DiskLruCache diskLruCache;
@SuppressWarnings("deprecation")
public static DiskCache create(File directory, long maxSize) {
return new DiskLruCacheWrapper(directory, maxSize);
}
@Deprecated
@SuppressWarnings({"WeakerAccess", "DeprecatedIsStillUsed"})
protected DiskLruCacheWrapper(File directory, long maxSize) {
this.directory = directory;
this.maxSize = maxSize;
this.safeKeyGenerator = new SafeKeyGenerator();
}
private synchronized DiskLruCache getDiskCache() throws IOException {
if (diskLruCache == null) {
diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
}
return diskLruCache;
}
@Override
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
...
}
return result;
}
@Override
public void put(Key key, Writer writer) {
String safeKey = safeKeyGenerator.getSafeKey(key);
writeLocker.acquire(safeKey);
try {
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
...
DiskLruCache.Editor editor = diskCache.edit(safeKey);
...
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
...
}
} finally {
writeLocker.release(safeKey);
}
}
...
}
里面包裝了一個DiskLruCache贱鄙,該類主要是為DiskLruCache提供了一個根據(jù)Key生成safeKey的SafeKeyGenerator以及寫鎖DiskCacheWriteLocker。
回到GlideBuilder.build(Context)中姨谷,diskCacheFactory會被傳進(jìn)Engine中逗宁,在Engine的構(gòu)造方法中會被包裝成為一個LazyDiskCacheProvider
,
在被需要的時候調(diào)用getDiskCache()方法梦湘,這樣就會調(diào)用factory的build()方法返回一個DiskCache瞎颗。代碼如下:
# Engine.java
Engine(
MemoryCache cache,
DiskCache.Factory diskCacheFactory,
GlideExecutor diskCacheExecutor,
GlideExecutor sourceExecutor,
GlideExecutor sourceUnlimitedExecutor,
GlideExecutor animationExecutor,
Jobs jobs,
EngineKeyFactory keyFactory,
ActiveResources activeResources,
EngineJobFactory engineJobFactory,
DecodeJobFactory decodeJobFactory,
ResourceRecycler resourceRecycler,
boolean isActiveResourceRetentionAllowed) {
this.cache = cache;
this.diskCacheProvider = new LazyDiskCacheProvider(diskCacheFactory);
...
// 省略部分代碼
}
private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider {
// 省略部分代碼
...
@Override
public DiskCache getDiskCache() {
if (diskCache == null) {
synchronized (this) {
if (diskCache == null) {
diskCache = factory.build();
}
if (diskCache == null) {
diskCache = new DiskCacheAdapter();
}
}
}
return diskCache;
}
}
LazyDiskCacheProvider會在Engine后面的初始化流程中作為入?yún)鞯紻ecodeJobFactory的構(gòu)造器。在DecodeJobFactory創(chuàng)建DecodeJob時也會作為入?yún)鬟M(jìn)去捌议,DecodeJob中會以全局變量保存此LazyDiskCacheProvider哼拔,在資源加載完畢并展示后,會進(jìn)行緩存的存儲瓣颅。同時倦逐,DecodeJob也會在DecodeHelper初始化時,將此DiskCacheProvider設(shè)置進(jìn)去宫补,供ResourceCacheGenerator檬姥、DataCacheGenerator讀取緩存,供SourceGenerator寫入緩存粉怕。
3. ActiveResources
ActiveResources在Engine的構(gòu)造器中被創(chuàng)建健民,在ActiveResources的構(gòu)造器中會啟動一個后臺優(yōu)先級級別(THREAD_PRIORITY_BACKGROUND)的線程,在該線程中會調(diào)用cleanReferenceQueue()方法一直循環(huán)清除ReferenceQueue中的將要被GC的Resource贫贝。
ActiveResources(
boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
monitorClearedResourcesExecutor.execute(
new Runnable() {
@Override
public void run() {
cleanReferenceQueue();
}
});
}
先來看看ActiveResources的activate方法(保存)秉犹、deactivate方法(刪除)的方法。
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
synchronized void deactivate(Key key) {
ResourceWeakReference removed = activeEngineResources.remove(key);
if (removed != null) {
removed.reset();
}
}
activate方法會將參數(shù)封裝成為一個ResourceWeakReference平酿,然后放入map中凤优,如果對應(yīng)的key之前有值悦陋,那么調(diào)用之前值的reset方法進(jìn)行清除蜈彼。deactivate方法先在map中移除,然后調(diào)用resource的reset方法進(jìn)行清除俺驶。ResourceWeakReference繼承WeakReference幸逆,內(nèi)部只是保存了Resource的一些屬性。
圖片資源是什么時候從ActiveResource中緩存至內(nèi)存的暮现?
ResourceWeakReference繼承WeakReference还绘,內(nèi)部只是保存了Resource的一些屬性。
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
@SuppressWarnings("WeakerAccess") @Synthetic final Key key;
@SuppressWarnings("WeakerAccess") @Synthetic final boolean isCacheable;
@Nullable @SuppressWarnings("WeakerAccess") @Synthetic Resource<?> resource;
@Synthetic
@SuppressWarnings("WeakerAccess")
ResourceWeakReference(
@NonNull Key key,
@NonNull EngineResource<?> referent,
@NonNull ReferenceQueue<? super EngineResource<?>> queue,
boolean isActiveResourceRetentionAllowed) {
// focus
super(referent, queue);
this.key = Preconditions.checkNotNull(key);
this.resource =
referent.isCacheable() && isActiveResourceRetentionAllowed
? Preconditions.checkNotNull(referent.getResource()) : null;
isCacheable = referent.isCacheable();
}
}
其構(gòu)造方法中調(diào)用了super(referent, queue)栖袋,這樣做可以讓將要被GC的對象放入到ReferenceQueue中拍顷。而ActiveResources.cleanReferenceQueue()
方法會一直嘗試從queue中獲取將要被GC的resource,然后調(diào)用cleanupActiveReference
方法將resource從activeEngineResources
中移除塘幅。cleanupActiveReference方法代碼如下:
#ActiveResource.java
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (listener) {
synchronized (this) {
// 移除active資源
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
// 構(gòu)造新的 Resource
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
// 回調(diào)Engine的onResourceReleased方法
// 這會導(dǎo)致此資源從active變成memory cache狀態(tài)
listener.onResourceReleased(ref.key, newResource);
}
}
}
Engine實現(xiàn)了EngineResource.ResourceListener昔案,此處的listener就是Engine尿贫,最終會回調(diào)Engine.onResourceReleased。
#Engine.java
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
如果資源可以被緩存踏揣,則緩存到 memory cache庆亡,否則對資源進(jìn)行回收。
也就是說當(dāng)前圖片資源未在使用中時捞稿,會先從ActiveResource的弱引用緩存中將其移除又谋,然后將該資源添加到內(nèi)存緩存中。
緩存的存取過程
內(nèi)存緩存
現(xiàn)在我們來分析下緩存的存取流程娱局,加載開始入口從Engine.load()開始彰亥,先看下對這個方法的注釋:
- 會先檢查(Active Resources),如果有就直接返回衰齐,Active Resources沒有被引用的資源會放入Memory Cache剩愧,如果Active Resources沒有,會往下走娇斩。
- 檢查Memory Cache中是否有需要的資源仁卷,如果有就返回,Memory Cache中沒有就繼續(xù)往下走犬第。
- 檢查當(dāng)前在運行中的job中是否有該資源的下載锦积,有就在現(xiàn)有的job中直接添加callback返回,不重復(fù)下載歉嗓,當(dāng)然前提是計算得到的key是一致的丰介,如果還是沒有,就會構(gòu)造一個新的job開始新的工作鉴分。
public synchronized <R> LoadStatus load(...) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
// focus1
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
// focus2
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
// focus3
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(...);
DecodeJob<R> decodeJob =
decodeJobFactory.build(...);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
// focus 4
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
先看到 focus 1,這一步會從 ActiveResources 中加載資源哮幢,首先判斷是否使用內(nèi)存緩存,否的話返回null志珍;否則到 ActiveResources 中取數(shù)據(jù):
// Engine.java
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
接著回到Engine.load()中繼續(xù)看到focus 2,如果在cache中找到就是remove掉橙垢,然后返回EngineResource,其中需要EngineResource進(jìn)行acquire一下伦糯,這個后面再看柜某,然后會把資源移到ActiveResources中,也就是上面提到的緩存:
// Engine.java
private final MemoryCache cache;
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
}
return result;
}
其中cache是MemoryCache接口的實現(xiàn)敛纲,如果沒設(shè)置喂击,默認(rèn)在build的時候是LruResourceCache, 也就是熟悉的LRU Cache:
// GlideBuilder.java
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
關(guān)于LruCache,前面已經(jīng)分析過了淤翔。
回到Engine.load方法翰绊,從緩存加載成功后的回調(diào)cb.onResourceReady(cached, DataSource.MEMORY_CACHE);可以看到:active狀態(tài)的資源和memory cache狀態(tài)的資源都是DataSource.MEMORY_CACHE,并且加載的資源都是 EngineResource 對象,該對象內(nèi)部采用了引用計數(shù)去判斷資源是否被釋放监嗜,如果引用計數(shù)為0琳要,那么會調(diào)用listener.onResourceReleased(key, this)方法通知外界此資源已經(jīng)釋放了。這里的listener是ResourceListener類型的接口秤茅,只有一個onResourceReleased(Key key, EngineResource resource)方法稚补,Engine實現(xiàn)了該接口,此處的listener就是Engine框喳。在Engine.onResourceReleased方法中會判斷資源是否可緩存课幕,可緩存則將此資源放入memory cache中,否則回收掉該資源五垮,代碼如下:
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
// 從activeResources中移除
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
// 存入 MemoryCache
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
思路再拉到Engine.load()的流程中乍惊,第一次加載的時候activeResources和memoryCache中都沒有緩存的,判斷當(dāng)前是否有EngineJob在運行放仗,,如果job已經(jīng)在運行了润绎,那么直接添加一個回調(diào)后返回LoadStatus,這個可以允許用戶取消任務(wù):
// Engine.java
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// LoadStatus
public static class LoadStatus {
private final EngineJob<?> engineJob;
private final ResourceCallback cb;
LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {
this.cb = cb;
this.engineJob = engineJob;
}
public void cancel() {
engineJob.removeCallback(cb);
}
}
接著往下看到focus 4, 到這里就需要創(chuàng)建后臺任務(wù)去拉取磁盤文件或者發(fā)起網(wǎng)絡(luò)請求。具體就是通過DecodeJob和EngineJob去加載資源诞挨。
DecoceJob實現(xiàn)了Runnable接口莉撇,然后會被EngineJob.start方法提交到對應(yīng)的線程池中去執(zhí)行。在DecoceJob的run方法中惶傻,會依次從
ResourceCacheGenerator
和DataCacheGenerator
中去取緩存數(shù)據(jù)棍郎,當(dāng)這兩者都取不到的情況下,會交給```SourceGenerator````加載網(wǎng)絡(luò)圖片或者本地資源银室。resource資源和data資源都是磁盤緩存中的資源涂佃。
ResourceCacheGenerator 、DataCacheGenerator和SourceGenerator
這里
ResourceCacheGenerator
蜈敢、DataCacheGenerator
辜荠、SourceGenerator
均實現(xiàn)了DataFetcherGenerator
接口。
- ResourceCacheGenerator:從已經(jīng)完成剪裁或者轉(zhuǎn)換的資源緩存文件中生成DataFetchers抓狭;
- DataCacheGenerator:從未修改的原始源數(shù)據(jù)的緩存文件中生成DataFetchers伯病;
- SourceGenerator:從原始數(shù)據(jù)中生成DataFetchers;
這三個類中的前兩個就是Glide三級緩存中的第二級緩存辐宾,文件緩存狱从,前者ResourceCacheGenerator存儲的是經(jīng)過剪裁或轉(zhuǎn)換的圖片數(shù)據(jù),后者DataCacheGenerator存儲的就是未經(jīng)任何修改的原始數(shù)據(jù)叠纹。正好對應(yīng)著磁盤緩存策略中的RESOURCE和DATA。
什么是DataFetcher敞葛?
就是數(shù)據(jù)加載器誉察,他有個
loadData
方法用來從資源加載數(shù)據(jù),這個方法必須運行在子線程中惹谐,如果緩存中沒有數(shù)據(jù)持偏,就會通過這個方法去加載原始數(shù)據(jù)驼卖。
舉個例子,比如要通過一個網(wǎng)絡(luò)url圖片地址加載圖片鸿秆,那么需要通過SourceGenerator
生成相應(yīng)的DataFetcher
通過網(wǎng)絡(luò)獲取數(shù)據(jù)酌畜;從網(wǎng)絡(luò)上獲取到數(shù)據(jù)流后,不進(jìn)行任何改變會被存儲到本地文件緩存中(這個本地文件默認(rèn)的目錄是context.getCacheDir()方法獲取到的目錄卿叽,文件大小250M)桥胞,這個存儲之前先修改runReason=SWITCH_TO_SOURCE_SERVICE
,然后重啟一個線程考婴,通過SourceGenerator
對象去做存儲操作贩虾,而DataCacheGenerator
就表示獲取存儲的數(shù)據(jù),然后經(jīng)過解碼然后將數(shù)據(jù)顯示到imageview沥阱。操作完成后會往內(nèi)存中再存一份數(shù)據(jù)缎罢,那么ResourceCacheGenerator
就表示獲取這些已經(jīng)經(jīng)過解碼剪裁的緩存文件。
磁盤緩存
先構(gòu)造兩個job考杉,一個是EngineJob,另外一個DecodeJob,其中DecodeJob會根據(jù)需要解碼的資源來源分成下面幾個階段:
private enum Stage {
/** The initial stage. */
INITIALIZE,
/** Decode from a cached resource. */
RESOURCE_CACHE,
/** Decode from cached source data. */
DATA_CACHE,
/** Decode from retrieved source. */
SOURCE,
/** Encoding transformed resources after a successful load. */
ENCODE,
/** No more viable stages. */
FINISHED,
}
在構(gòu)造DecodeJob時會把狀態(tài)置為INITIALIZE策精。
構(gòu)造完兩個 Job 后會調(diào)用 EngineJob.start
(DecodeJob),首先會調(diào)用getNextStage來確定下一個階段崇棠,這里面跟DiskCacheStrategy這個傳入的磁盤緩存策略有關(guān)蛮寂。
磁盤策略有下面幾種:
- ALL: 緩存原始數(shù)據(jù)和轉(zhuǎn)換后的數(shù)據(jù)
- NONE: 不緩存
- DATA: 原始數(shù)據(jù),未經(jīng)過解碼或者轉(zhuǎn)換
- RESOURCE: 緩存經(jīng)過解碼的數(shù)據(jù)
- AUTOMATIC(默認(rèn)): 根據(jù)
EncodeStrategy
和DataSource
等條件自動選擇合適的緩存方
默認(rèn)的AUTOMATIC方式是允許解碼緩存的RESOURCE:
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
// 省略部分代碼
@Override
public boolean decodeCachedResource() {
return true;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
所以在 getNextStage 會先返回
Stage.RESOURCE_CACHE,然后在start中會返回diskCacheExecutor,然后開始執(zhí)行DecodeJob:
// EngineJob.java
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
//到這里之前一直都在主線程易茬,下面這句代碼就會切換到子線程
executor.execute(decodeJob);
}
// DecodeJob.java
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
DecodeJob會回調(diào)run()開始執(zhí)行, run()中調(diào)用runWrapped執(zhí)行工作,這里runReason還是RunReason.INITIALIZE 酬蹋,根據(jù)前面的分析指導(dǎo)這里會獲得一個ResourceCacheGenerator,然后調(diào)用runGenerators:
// DecodeJob.java
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
在 runGenerators 中,會調(diào)用 startNext,目前
currentGenerator是ResourceCacheGenerator, 那么就是調(diào)用它的startNext方法:
@Override
public boolean startNext() {
// list里面只有一個GlideUrl對象
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
// 獲得了三個可以到達(dá)的registeredResourceClasses
// GifDrawable抽莱、Bitmap范抓、BitmapDrawable
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
throw new IllegalStateException(
"Failed to find any load path from " + helper.getModelClass() + " to "
+ helper.getTranscodeClass());
}
// 遍歷sourceIds中的每一個key、resourceClasses中每一個class食铐,以及其他的一些值組成key
// 嘗試在磁盤緩存中以key找到緩存文件
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
// 構(gòu)造key
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
// 查找緩存文件
cacheFile = helper.getDiskCache().get(currentKey);
// 如果找到了緩存文件匕垫,循環(huán)條件則會為false,退出循環(huán)
if (cacheFile != null) {
sourceKey = sourceId;
// 1. 找出注入時以File.class為modelClass的注入代碼
// 2. 調(diào)用所有注入的factory.build方法得到ModelLoader
// 3 .過濾掉不可能處理model的ModelLoader
// 此時的modelLoaders值為:
// [ByteBufferFileLoader, FileLoader, FileLoader, UnitModelLoader]
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
// 如果找到了緩存文件虐呻,hasNextModelLoader()方法則會為true象泵,可以執(zhí)行循環(huán)
// 沒有找到緩存文件,則不會進(jìn)入循環(huán)斟叼,會直接返回false
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
// 在循環(huán)中會依次判斷某個ModelLoader能不能加載此文件
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
// 如果某個ModelLoader可以偶惠,那么就調(diào)用其fetcher進(jìn)行加載數(shù)據(jù)
// 加載成功或失敗會通知自身
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
找緩存時key的類型為ResourceCacheKey,生成ResourceCacheKey之后會根據(jù)key去磁盤緩存中查找cacheFile = helper.getDiskCache().get(currentKey);
helper.getDiskCache()返回DiskCache接口朗涩,它的實現(xiàn)類是DiskLruCacheWrapper忽孽,看下DiskLruCacheWrapper.get方法。
# DiskLruCacheWrapper.java
@Override
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
...
File result = null;
try {
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
...
}
return result;
}
這里調(diào)用SafeKeyGenerator生成了一個String類型的SafeKey,實際上就是對原始key中每個字段都使用SHA-256加密兄一,然后將得到的字節(jié)數(shù)組轉(zhuǎn)換為16進(jìn)制的字符串厘线。生成SafeKey后,接著根據(jù)SafeKey去DiskCache里面找對應(yīng)的緩存文件出革,然后返回文件造壮。
回到ResourceCacheGenerator.startNext
方法中,如果找到了緩存會調(diào)用loadData.fetcher.loadData(helper.getPriority(), this);
這里的 fetcher 是 ByteBufferFetcher
骂束,ByteBufferFetcher
的loadData方法中最終會執(zhí)行callback.onDataReady(result)
這里callback是ResourceCacheGenerator
耳璧。
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE,
currentKey);
}
ResourceCacheGenerator
的onDataReady
方法又會回調(diào)DecodeJob
的onDataFetcherReady
方法進(jìn)行后續(xù)的解碼操作。
如果ResourceCacheGenerator沒有找到緩存栖雾,就會交給DataCacheGenerator繼續(xù)查找緩存楞抡。該類大體流程和ResourceCacheGenerator一樣,有點不同的是析藕,DataCacheGenerator的構(gòu)造器有兩個構(gòu)造器召廷,其中的DataCacheGenerator(List, DecodeHelper, FetcherReadyCallback)
構(gòu)造器是給SourceGenerator準(zhǔn)備的。因為如果沒有磁盤緩存账胧,那么從源頭加載后竞慢,肯定需要進(jìn)行磁盤緩存操作的。所以治泥,SourceGenerator會將加載后的資源保存到磁盤中筹煮,然后轉(zhuǎn)交給DataCacheGenerator從磁盤中取出交給ImageView展示。
看下DataCacheGenerator.startNext:
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
...
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
...
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這里的originalKey是DataCacheKey類型的居夹,DataCacheKey構(gòu)造方法如下:
DataCacheKey(Key sourceKey, Key signature)
這里的sourceKey和signature與ResourceCacheKey中的兩個變量一致败潦,從這里就可以看出:DataCache緩存的是原始的數(shù)據(jù),ResourceCache緩存的是是被解碼准脂、轉(zhuǎn)換后的數(shù)據(jù)劫扒。
如果DataCacheGenerator沒有取到緩存,那么會交給SourceGenerator從源頭加載狸膏。
總結(jié)一下:
- 首先在ResourceCacheGenerator沒有取到緩存沟饥,那么它的startNext就會返回false,在runGenerators中就會進(jìn)入循環(huán)體內(nèi):
- 接著會重復(fù)上面執(zhí)行g(shù)etNextStage,由于現(xiàn)在Stage已經(jīng)是RESOURCE_CACHE,所以接下來會返回DataCacheGenerator,執(zhí)行邏輯和上面的ResourceCacheGenerator是一樣的湾戳,如果還是沒有找到需要的贤旷,進(jìn)入循環(huán)體內(nèi)。
- 此時getNextStage會根據(jù)用于是否設(shè)置只從磁盤中獲取資源砾脑,如果是就會通知失敗幼驶,回調(diào)onLoadFailed;如果不是就設(shè)置當(dāng)前Stage為Stage.SOURCE,接著往下走拦止。
- 狀態(tài)就會進(jìn)入循環(huán)內(nèi)部的if條件邏輯里面县遣,調(diào)用reschedule糜颠。
- 在reschedule把runReason設(shè)置成SWITCH_TO_SOURCE_SERVICE,然后通過callback回調(diào)汹族。
#DecodeJob.runGenerators()
if (stage == Stage.SOURCE) {
reschedule();
return;
}
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
- DecodeJob中的callback是EngineJob傳遞過來的萧求,所以現(xiàn)在返回到EngineJob。
-在EngineJob中通過
getActiveSourceExecutor切換到網(wǎng)絡(luò)線程池中顶瞒,注意在此之前執(zhí)行文件緩存的線程池一直都是diskCacheExecutor夸政,執(zhí)行DecodeJob,下面就準(zhǔn)備開始發(fā)起網(wǎng)絡(luò)請求榴徐。
#EngineJob.java
@Override
public void reschedule(DecodeJob<?> job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
getActiveSourceExecutor().execute(job);
}
看下SourceGenerator的startNext方法守问。
@Override
public boolean startNext() {
// 首次運行dataToCache為null
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
// 首次運行sourceCacheGenerator為null
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這里的DataFetcher其實是HttpUrlFetcher,由它來發(fā)起網(wǎng)絡(luò)請求坑资。
// HttpUrlFetcher.java
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
加載成功后耗帕,依然會回調(diào)SourceGenerator的onDataReady方法。
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// cb 為 DecodeJob
cb.reschedule();
} else {
// cb 為 DecodeJob
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
先判斷獲取到的數(shù)據(jù)是否需要進(jìn)行磁盤緩存袱贮,如果需要磁盤緩存仿便,則經(jīng)過DecodeJob、EngineJob的調(diào)度攒巍,重新調(diào)用SourceGenerator.startNext方法嗽仪,此時dataToCache已經(jīng)被賦值,則會調(diào)用cacheData(data);進(jìn)行磁盤緩存的寫入柒莉,并轉(zhuǎn)交給DataCacheGenerator完成后續(xù)的處理;否則就通知DecodeJob已經(jīng)加載成功闻坚。
DataCacheGenerator前面已經(jīng)分析過了,就是用來加載本地原始數(shù)據(jù)的兢孝。
先看下SourceGenerator的startNext方法中調(diào)用的SourceGenerator.cacheData(data)窿凤。
# SourceGenerator.java
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
//獲取編碼器
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
//獲取寫入器
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
//根據(jù)data,key,signature 創(chuàng)建緩存key。這個key是DataCacheKey類型的跨蟹,存儲是原始數(shù)據(jù)
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
//獲取硬盤緩存并開始緩存
helper.getDiskCache().put(originalKey, writer);
...
} finally {
loadData.fetcher.cleanup();
}
//賦值
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
cacheData方法先構(gòu)建了一個DataCacheKey將data寫入了磁盤雳殊,然后new了一個DataCacheGenerator賦值給sourceCacheGenerator∨缡校回到startNext繼續(xù)向下執(zhí)行相种,此時sourceCacheGenerator不為空,就調(diào)用其startNext()方法從磁盤中加載剛寫入磁盤的數(shù)據(jù)品姓,并返回true讓DecodeJob停止嘗試獲取數(shù)據(jù)寝并。此時,從磁盤緩存中讀取數(shù)據(jù)的邏輯已經(jīng)完成腹备,接下來是寫磁盤緩存衬潦。
結(jié)論
Glide加載網(wǎng)絡(luò)數(shù)據(jù)后, 在返回之前植酥,會先緩存一份原始數(shù)據(jù) 镀岛。
磁盤緩存與內(nèi)存緩存的關(guān)聯(lián)
磁盤緩存結(jié)束后弦牡,sourceCacheGenerator被賦值,然后返回漂羊,回到startNext方法驾锰,接著往下走,會執(zhí)行sourceCacheGenerator的startNext方法走越,
此方法最終調(diào)用loadData.fetcher.loadData(helper.getPriority(), this);這句話椭豫。由于data是流的類型,所以看ByteBufferFetcher
package com.bumptech.glide.load.model.ByteBufferFileLoader.ByteBufferFetcher
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
//讀取文件獲取結(jié)果 是ByteBuffer類型
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
callback.onLoadFailed(e);
return;
}
//回調(diào)
callback.onDataReady(result);
}
最終會調(diào)用到下面的方法
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
//如果不是同一個線程旨指,最終會修改runReason為RunReason.DECODE_DATA;并從起一個線程執(zhí)行decodeFromRetrievedData方法
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
try {
//如果是同一個線程 直接執(zhí)行decodeFromRetrievedData方法
decodeFromRetrievedData();
} finally {
}
}
}
decodeFromRetrievedData
中主要調(diào)用了notifyEncodeAndRelease
方法赏酥,在后一個方法中會執(zhí)行到notifyComplete
方法,notifyComplete
最終會調(diào)用到notifyCallbacksOfResult
方法,該方法調(diào)用下面的回調(diào)方法:
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
//將資源緩存到activeResource中谆构,這里就跟前面的”先從內(nèi)存的二級緩存加載數(shù)據(jù)“的邏輯對應(yīng)上了
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
在這里會將資源放入activeResources中裸扶,資源變?yōu)閍ctive狀態(tài)。后面會使用Executors.mainThreadExecutor()調(diào)用SingleRequest.onResourceReady回調(diào)進(jìn)行資源的顯示搬素。在觸發(fā)回調(diào)前后各有一個地方會對engineResource進(jìn)行acquire()和release()操作呵晨,這兩個操作分別發(fā)生在notifyCallbacksOfResult()方法的incrementPendingCallbacks、decrementPendingCallbacks()調(diào)用中蔗蹋。
CallResourceReady的run方法中也會調(diào)用engineResource.acquire()何荚,上面的代碼調(diào)用結(jié)束后,engineResource的引用計數(shù)為1猪杭。engineResource的引用計數(shù)會在RequestManager.onDestory方法中最終調(diào)用SingleRequest.clear()方法餐塘,SingleRequest.clear()內(nèi)部調(diào)用releaseResource()、Engine.release 進(jìn)行釋放皂吮,這樣引用計數(shù)就變?yōu)?戒傻。引用計數(shù)就變?yōu)?后會通知Engine將此資源從active狀態(tài)變成memory cache狀態(tài)。如果我們再次加載資源時可以從memory cache中加載蜂筹,那么資源又會從memory cache狀態(tài)變成active狀態(tài)需纳。也就是說,在資源第一次顯示后艺挪,我們關(guān)閉頁面不翩,資源會由active變成memory cache;然后我們再次進(jìn)入頁面,加載時會命中memory cache麻裳,從而又變成active狀態(tài)口蝠。
小結(jié)一下
Glide的三級緩存:第一級緩存內(nèi)存緩存,內(nèi)存緩存又分為兩種存儲形式津坑,弱引用緩存和LRU策略的緩存方式妙蔗;第二級緩存是本地文件緩存,緩存的文件大小默認(rèn)是250M疆瑰,本地緩存會分為存儲未經(jīng)修改的源數(shù)據(jù)和經(jīng)過剪裁后的數(shù)據(jù)眉反;第三級緩存就是網(wǎng)絡(luò)/數(shù)據(jù)源緩存昙啄。
關(guān)于緩存及展示數(shù)據(jù):數(shù)據(jù)加載出來之后,會先在緩存到本地文件中寸五,然后再去本地緩存文件中讀取數(shù)據(jù)進(jìn)行編碼梳凛,將編碼后的數(shù)據(jù)再進(jìn)行一次緩存,接著再內(nèi)存中進(jìn)行緩存播歼,最后將數(shù)據(jù)編碼后的圖片文件發(fā)往主線程設(shè)置給target伶跷。
回答以下幾個問題:
Glide網(wǎng)絡(luò)請求回來后數(shù)據(jù)直接返回給用戶還是先存再返回
不是直接返回給用戶掰读,會在SourceGenerator中構(gòu)造一個DataCacheGenerator來取數(shù)據(jù)秘狞。簡單來說就是先緩存到磁盤,再從磁盤里取數(shù)據(jù)返回給ImageView添加圖片
Glide本地文件IO和網(wǎng)絡(luò)請求是一個線程嗎蹈集?
明顯不是烁试,本地IO通過diskCacheExecutor,而網(wǎng)絡(luò)IO通過ActiveSourceExecutor
總結(jié)
讀取內(nèi)存緩存時,先從弱引用機(jī)制的內(nèi)存緩存讀取拢肆,再從LruCache算法機(jī)制的內(nèi)存緩存讀取
寫入內(nèi)存緩存時减响,先寫入 弱引用機(jī)制 的內(nèi)存緩存,等到圖片不再被使用時郭怪,再寫入到 LruCache算法機(jī)制的內(nèi)存緩存;
讀取磁盤緩存時支示,先讀取轉(zhuǎn)換后圖片的緩存,再讀取原始圖片的緩存鄙才。