預(yù)備知識:
假設(shè)已經(jīng)熟悉了如下概念事秀,不懂暫時這么理解也可以
圖片展示的Bitmap作喘,Drawable這種最終能被安卓ImageView識別用來加載的對象琢蛤,glide用抽象Resource表示蜒犯,也就是內(nèi)存緩存是一個個Resource對象
流程加載如下
Engine.load()分析
1 加載圖片先從內(nèi)存當(dāng)前展示緩存activeResources.get(key)讀取
2 加載圖片再從內(nèi)存MemoryCache讀取,可能此緩存已經(jīng)不在界面展示
3 開啟DecodeJob,本質(zhì)上一個線程郑叠,所以會使用線程池加載,
加載之前判斷是否已經(jīng)存在一個正在運行的加載線程,因為可能存在多個加載一模一樣url的情況晕粪。
這個線程內(nèi)部先判斷文件緩存是否存在,否則網(wǎng)絡(luò)加載
為方便理解渐裸,本文內(nèi)存2種緩存類型巫湘,以展示緩存,不展示緩存區(qū)分
/**
Check the current set of actively used resources, return the active resource if
present, and move any newly inactive resources into the memory cache
Check the memory cache and provide the cached resource if present
Check the current set of in progress loads and add the cb to the in progress load if one is present.
Start a new load.
* Active resources are those that have been provided to at least one request and have not yet
* been released. Once all consumers of a resource have released that resource, the resource then
* goes to cache. If the resource is ever returned to a new consumer from cache, it is re-added to
* the active resources. If the resource is evicted from the cache, its resources are recycled and
* re-used if possible and the resource is discarded. There is no strict requirement that
* consumers release their resources so active resources are held weakly.
**/
public <R> LoadStatus load() {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//加載activeResource
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//加載MemoryCache
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
//當(dāng)前已經(jīng)存在一個 加載線程昏鹃,直接添加監(jiān)聽即可
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
return new LoadStatus(cb, current);
}
//觸發(fā)一個線程加載
EngineJob<R> engineJob =
engineJobFactory.build();
DecodeJob<R> decodeJob =
decodeJobFactory.build();
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//線程池加載線程
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
內(nèi)存上的activeResource和MemoryCache區(qū)別
如果當(dāng)前加載一張圖片成功尚氛,當(dāng)前界面展示出來。
此時Engine監(jiān)聽到完成洞渤,觸發(fā)activeResource添加操作
public 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.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
如果這個ImageView重新渲染一張圖片阅嘶,那么之前上一張圖片資源就需要回收,
類似這種情況载迄,都會觸發(fā)從activeResource刪除讯柔,而往MemoryCache添加
如RequestBuilder.into(),也就是最常見的Glide.with(context).load(url).into(ImgView);
private <Y extends Target<TranscodeType>> Y into()
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
....
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
如上都是先clear再track,requestManager.clear(target);
RequestManager 調(diào)用clear觸發(fā)Request的clear护昧,track觸發(fā)Request的begin魂迄,
這樣再分析一下SingleRequest對應(yīng)操作active緩存的刪除,對應(yīng)MemoryCache添加
public void clear() {
if (resource != null) {
releaseResource(resource);
}
}
private void releaseResource(Resource<?> resource) {
engine.release(resource);
this.resource = null;
}
void release() {
if (--acquired == 0) {
//監(jiān)聽器是Engine
listener.onResourceReleased(key, this);
}
}
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
本地文件寫入緩存時機
一般內(nèi)存與本地緩存都沒有需要觸發(fā)網(wǎng)絡(luò)請求惋耙,那么進入SourceGenerator這個處理網(wǎng)絡(luò)請求的捣炬,實際上是SourceFetcher處理,處理完成绽榛,走到SourceGenerator監(jiān)聽回調(diào)遥金,
這時候觀察一下,思考一下邏輯蒜田,網(wǎng)絡(luò)加載完成稿械,便存入本地
使用的也是LRU緩存。
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
cacheData就是調(diào)用LRU文件觸發(fā)保存文件緩存
至于這個對象是從哪賦值的冲粤,參考如下美莫,走的是回調(diào),那回調(diào)是這么觸發(fā)梯捕,一般每個Generator都是走內(nèi)部Fetcher工作然后回調(diào)給自己
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
}
}
網(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);
}
這樣一樣厢呵。回調(diào)返回的是InputStream傀顾,那么就需要琢磨怎么把這個InputStream變成保存文件
StreamEncode實現(xiàn)將InputStream轉(zhuǎn)成文件襟铭。如下
public boolean encode() {
byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
int read;
while ((read = data.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
os.close();
success = true;
} catch (){
...}
byteArrayPool.put(buffer);
}
return success;
}
本地文件讀取時機
內(nèi)存沒有,則觸發(fā)本地讀取。
如果通過映射關(guān)系找到本地已經(jīng)有這個文件寒砖,需要加載這個文件資源赐劣。
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);
}
}
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
LRU讀寫這里不再展開,只知道使用這種緩存策略
原圖哩都,轉(zhuǎn)圖魁兼,也不展開了
原圖即是網(wǎng)絡(luò)下載的圖片,
實際上展示可能需要轉(zhuǎn)碼壓縮之類的
總結(jié)
- 內(nèi)存分為當(dāng)前已經(jīng)展示的緩存和界面不展示但是內(nèi)存會存下的緩存漠嵌。
- 耗時的操作放在單獨線程操作咐汞,有本地文件緩存和網(wǎng)絡(luò)獲取。
- 如果網(wǎng)絡(luò)獲取觸發(fā)文件保存儒鹿。
- 一個imageview加載完成化撕,觸發(fā)active緩存添加
- 一個imageview加載第二張圖片,觸發(fā)active緩存刪除约炎,MemoryCache緩存添加