今天我要對著 Glide數(shù)據(jù)加載 進(jìn)行一頓暴講耘沼,我要給它講的 锃光瓦亮韧衣,我要給它講的 烏漆嘛黑县遣。
上回針對Gldie的準(zhǔn)備工作進(jìn)行了詳細(xì)分解,從使用到原理汹族,從整體到局部整體噴了一遍萧求。
還是不太懂得小伙伴可以飛機(jī)直達(dá) 對著那Glide最新版本就是一頓暴講 ,因為之前的處理不太了解的話顶瞒,會對接下來的神邏輯產(chǎn)生困擾夸政。
當(dāng)然了,如果此刻確實困了的同學(xué)榴徐,我一定給你一個 好夢...
以下的幾個標(biāo)題是在我寫完整篇后重新總結(jié)的守问,這樣會比較有針對性。
- 緩存的讀取
- 請求的啟動
- 請求成功后的數(shù)據(jù)處理(InputStream的處理)
- 數(shù)據(jù)解碼
- 數(shù)據(jù)轉(zhuǎn)換
- 圖片顯示與那一堆回調(diào)
- 緩存的維護(hù)與使用流程
來坑资,我們開始搞...
兄臺準(zhǔn)備好了么耗帕?我要以Glide數(shù)據(jù)加載,祭我大諾克薩斯袱贮!
1. 緩存的讀取
緩存分為弱引用緩存和LRU緩存仿便。
前景回顧下,我們上文已經(jīng)分析到了 SingleReques.begin()
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
...
//判斷寬高是狗有效攒巍。
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//寬高無效嗽仪,需要等待有效
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
public void onSizeReady(int width, int height) {
...
//這個方法上次沒有展開分析,而這個方法即是整個加載的核心柒莉。
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
...
}
engine對象是在Glide.with()進(jìn)行初始化的闻坚,此時使用的engine即是Glide.with()初始化的對象。
我們跟進(jìn)看下 engine.load() 具體做了什么操作兢孝。
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//構(gòu)建唯一key窿凤,注意構(gòu)建過程的入?yún)ⅲ瑢捀呖缧罚刂扶ㄊ猓葏?shù)。
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass,
transcodeClass, options);
EngineResource<?> memoryResource;
synchronized (this) {
//加鎖喷市,從內(nèi)存中獲取,可直接看后續(xù)的跟進(jìn).
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//如果緩存中都沒有找到相种,則調(diào)用waitForExistingOrStartNewJob(),后續(xù)單獨展開分析。
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// 如果 engine lock 則避免回調(diào)寝并,防止調(diào)用方死鎖箫措。
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
//isMemoryCacheable默認(rèn)情況下為true
//當(dāng)配置中設(shè)置了RequestOptions.skipMemoryCacheOf()的值的話:
//1.當(dāng)skipMemoryCacheOf傳入true時為false,即關(guān)閉內(nèi)存緩存
//2.當(dāng)skipMemoryCacheOf傳入false時為true,即開啟內(nèi)存緩存
if (!isMemoryCacheable) {
return null;
}
//字面意思是從弱引用列表中獲取
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
//如果弱引用Map中沒有獲取到則從緩存中獲取
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
//從弱引用Map中獲取數(shù)據(jù)
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
//判斷當(dāng)前對象是否被回收衬潦,如果沒有被回收引用計數(shù)+1斤蔓,如已回收則拋異常。
cached.acquire();
//添加到弱引用Map中镀岛,方便下次使用弦牡。
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
//雖然調(diào)用的是cache.remove(key),但是刪除成功后會返回刪除的對象漂羊,如果沒有找到刪除對象則返回null
//此時的cache為LruResourceCache驾锰,是在Glide初始化時由GlideBuilder.build()時創(chuàng)建的。
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
//這些if判斷目前還不是特別清楚邏輯走越,需要知道cache數(shù)據(jù)怎么添加的才能清楚這些椭豫。
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, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
雖然現(xiàn)在不清楚緩存的數(shù)據(jù)在哪個位置添加的,但是我們清楚了緩存的調(diào)用順序旨指,先弱引用赏酥,后Lru,最后走網(wǎng)絡(luò)谆构。
- 首先通過 keyFactory.buildKey() 生成圖片的唯一key裸扶。
- 其次 loadFromActiveResources() 從activeResources(弱引用Map)中查找對應(yīng)的資源。
- 最后通過 loadFromCache()->getEngineResourceFromCache()從LruResourceCache刪除資源 搬素,如果刪除成功則返回原數(shù)據(jù)呵晨,并將數(shù)據(jù)添加到activeResources中,同時引用技術(shù)器+1蔗蹋。(此處用刪除代替查詢何荚,一舉兩得。)
意義與生俱來猪杭,我們只需遵其而行,真意妥衣,環(huán)繞四周皂吮。動則得福。
2. 請求的啟動
如果弱引用和Lru均沒獲取到數(shù)據(jù)需通過 waitForExistingOrStartNewJob()方法 進(jìn)行處理税手,我們繼續(xù)蜂筹。
private <R> LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor,
EngineKey key,
long startTime) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool,
useAnimationPool, onlyRetrieveFromCache);
//DecodeJob是一個Runnable對象,可以看到此處decodeJob被當(dāng)作參數(shù)傳入了 engineJob.start(decodeJob)中
DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext, model, key, signature,
width, height, resourceClass,
transcodeClass, priority, diskCacheStrategy,
transformations, isTransformationRequired,
isScaleOnlyOrNoTransform, onlyRetrieveFromCache,
options, engineJob);
jobs.put(key, engineJob);
//添加回調(diào)和回調(diào)線程池芦倒。
engineJob.addCallback(cb, callbackExecutor);
//啟動decodeJob這個Runnable
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
看到這里感覺懵逼很正常艺挪,我看到這也是懵逼的,咱們繼續(xù)兵扬,看看 engineJob.start(decodeJob);干了什么事
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//獲取GlideExecutor 線程池
GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
//開始執(zhí)行decodeJob這個Runnable
executor.execute(decodeJob);
}
/**
* DecodeJob.willDecodeFromCache()方法
* 如果從硬盤解碼資源返回true麻裳,從緩存中解碼返回false口蝠。
*/
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
真像就在decodeJob的run方法中.
public void run() {
// This should be much more fine grained, but since Java's thread pool implementation silently
// swallows all otherwise fatal exceptions, this will at least make it obvious to developers
// that something is failing.
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
//try語句中的方法可能使currentfeetcher無效,因此在此處設(shè)置一個局部變量以確保以任何方式清除該fetcher津坑。
DataFetcher<?> localFetcher = currentFetcher;
try {
//如果狀態(tài)為已取消的狀態(tài)則調(diào)用notifyFailed(),連帶調(diào)用callback.onLoadFailed(e);
if (isCancelled) {
notifyFailed();
return;
}
//關(guān)鍵方法
runWrapped();
} catch (CallbackException e) {
// 如果不是由Glide控制的回調(diào)拋出異常妙蔗,我們應(yīng)該避免下面的Glide特定調(diào)試邏輯。
throw e;
} catch (Throwable t) {
// 捕捉可拋出的對象疆瑰,并不是處理oom的例外眉反。我們在GlideExecutor中使用了.submit(),因此我們不會通過這樣做悄悄地隱藏崩潰穆役。
//但是寸五,我們要確保在加載失敗時始終通知回調(diào)。如果沒有這個通知耿币,未經(jīng)處理的拋出文件永遠(yuǎn)不會通知相應(yīng)的回調(diào)
//這可能會導(dǎo)致負(fù)載永遠(yuǎn)靜默掛起播歼,這種情況對于在后臺線程上使用Futures的用戶尤其不利。
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG,"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t);
}
// 如果當(dāng)前狀態(tài)是encoding掰读,則直接callback.onLoadFailed(e);
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
// 需要關(guān)閉localFetcher秘狞。
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
private void notifyFailed() {
setNotifiedOrThrow();
GlideException e = new GlideException("Failed to load resource", new ArrayList<>(throwables));
callback.onLoadFailed(e);
onLoadFailed();
}
上邊說了一大堆,大部分都是處理異常情況的邏輯蹈集,只有 runWrapped(); 才是核心方法烁试。
//初始化過程中runReason 的初始值為RunReason.INITIALIZE;
RunReason runReason = RunReason.INITIALIZE;
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);
}
}
//傳過來的是Stage.INITIALIZE
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:
// 如果用戶選擇僅從緩存檢索資源,則跳過從源加載.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
//此時的stage是Stage.SOURCE
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);
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
//此處的currentGenerator為SourceGenerator對象拢肆,在runWrapped() 的 switch方法中賦值的减响。
//startNext()是接口DataFetcherGenerator中的方法,只能看SourceGenerator實現(xiàn)接口后的具體邏輯
while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
跟進(jìn) SourceGenerator實現(xiàn)后的startNext()
@Override
public boolean startNext() {
//緩存判斷的一些邏輯
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
//放入緩存郭怪,構(gòu)建緩存key支示,存儲原圖等..這里就不展開分析了
cacheData(data);
}
//當(dāng)原始圖片放入磁盤緩存后,sourceCacheGenerator為DataCacheGenerator
//然后繼續(xù)執(zhí)行DataCacheGenerator的startNext方法
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
//沒有開啟磁盤緩存或獲取不到磁盤緩存的情況下
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//此處的helper為DecodeHelper對象鄙才,在DecodeJob.build()->DecodeJob.init()方法中提前進(jìn)行了創(chuàng)建胸完。
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//注意后續(xù)loadData對象實例化的部分。
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
//DecodeHelper.getLoadData()
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//modelLoaders 得到的是一個HttpGlideUrlLoader 對象列表具體可跟進(jìn)getModelLoaders()方法查看缓苛。同時可以倒推几睛。
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
//相當(dāng)于HttpGlideUrlLoader.buildLoadData();
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
//HttpGlideUrlLoader 類的buildLoadData 方法
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
// glideurl記錄解析的url,因此緩存它們可以節(jié)省一些對象實例化和解析url所花費的時間浓冒。
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
//此處為loadData對象實例化
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData( Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
//這個fetcher類型跟傳入的DataFetcher有關(guān)栽渴,此時傳入的是HttpUrlFetcher。
this.fetcher = Preconditions.checkNotNull(fetcher);
}
還記得上述的SourceGenerator.startNext()方法中的這一句么稳懒?
loadData.fetcher.loadData(helper.getPriority(), this);
此時的 loadData.fetcher為HttpUrlFetcher闲擦,為啥是HttpUrlFetcher上述的LoadData對象初始化已經(jīng)說明了。
//HttpUrlFetcher.loadData()
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//核心在這里
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//此時的callback為SourceGenerator
//因為調(diào)用loadData()的地方是在SourceGenerator.startNext()loadData.fetcher.loadData(helper.getPriority(), this)中
//調(diào)用時的入?yún)his則為SourceGenerator對象。
callback.onDataReady(result);
} catch (IOException e) {
...
callback.onLoadFailed(e);
} finally {
...
}
}
//我們繼續(xù)看下墅冷,快到頭了纯路。
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
//使用.equals比較URL會執(zhí)行額外的網(wǎng)絡(luò)I/O,并且通常會中斷可查看以下博客
//http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
//如果返回的狀態(tài)碼是200則直接返回數(shù)據(jù)流
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
//如果返回的是302重定向俺榆,則獲取重定向地址遞歸請求感昼。
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
至此網(wǎng)絡(luò)的加載回調(diào)的流程結(jié)束了,后續(xù)就是數(shù)據(jù)流的處理過程罐脊。
但是 DecodeJob.runWrapped() 整個過程有點亂定嗓,咱們捋一下。
- 通過RunReason.INITIALIZE判斷runWrapped()中的switch走第一個case萍桌。
- 調(diào)用getNextStage()獲得返回值為Stage.SOURCE
- 利用 2的返回值宵溅,調(diào)用 getNextGenerator()初始化SourceGenerator對象。
- 調(diào)用runGenerators()方法連帶調(diào)用currentGenerator.startNext()上炎,此時的currentGenerator為SourceGenerator對象恃逻。
- 在SourceGenerator.startNext()方法中,調(diào)用DecodeHelper.getLoadData().get(loadDataListIndex++);此處的DecodeHelper即是在DecodeJob.build()->DecodeJob.init()中初始化的DecodeHelper藕施。
- 在DecodeHelper.getLoadData()中獲取到HttpGlideUrlLoader列表對象(通過glideContext.getRegistry().getModelLoaders(model)獲取到)寇损。
- 循環(huán)HttpGlideUrlLoader列表,調(diào)用HttpGlideUrlLoader.buildLoadData()初始化LoadData對象裳食。
- 此時第5步的 helper.getLoadData().get()邏輯走完矛市,繼續(xù)SourceGenerator.startNext()中的loadData.fetcher.loadData(),loadData.fetcher類型依賴于LoadData初始化诲祸,此時的類型是HttpUrlFetcher浊吏,也就是HttpUrlFetcher.loadData();
-
- 在HttpUrlFetcher.loadData()中調(diào)用loadDataWithRedirects(),然后通過HttpURLConnection請求對應(yīng)的url地址獲取InputStream數(shù)據(jù)流。
- 9.1請求有兩種情況救氯,第一是直接返回狀態(tài)碼200找田,則直接返回數(shù)據(jù)流。
- 9.2狀態(tài)碼返回302着憨,通過getHeaderField("Location")獲取重定向的URL遞歸調(diào)用loadDataWithRedirects()進(jìn)行請求墩衙。
- 請求完成后,在HttpUrlFetcher.loadData()方法中調(diào)用callback.onDataReady(result);此時的callback是loadData()的入?yún)⑾砣印6?strong>HttpUrlFetcher.loadData()是在之前的SourceGenerator.startNext()中調(diào)用的底桂,并且傳了this,因此callback為SourceGenerator
總結(jié)完后是不是清晰了一些惧眠,上邊這一大堆,最重要的目的就是通過HttpURLConnection獲取到圖片的數(shù)據(jù)流于个,并通過回調(diào)返回氛魁。
不可久留一處,我遵循此道,直至終結(jié)
3.請求成功后的數(shù)據(jù)處理(InputStream的處理)
至此秀存,我們已經(jīng)拿到了網(wǎng)絡(luò)返回的InputStream數(shù)據(jù)流捶码,接下來我們看下返回的數(shù)據(jù)流到底咋用的?
//SourceGenerator.onDataReady()
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// 我們可能會接到其他回調(diào)或链。在此之前惫恼,我們應(yīng)該重新切回Glide的線程。
//cb是一個FetcherReadyCallback對象澳盐,在SourceGenerator構(gòu)造函數(shù)賦值祈纯,初始化在DecodeJob類中初始化,且傳了this叼耙,因此cb為DecodeJob腕窥。
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data,loadData.fetcher, loadData.fetcher.getDataSource(), originalKey);
}
}
//DecodeJob.reschedule()
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
//DecodeJob.onDataFetcherReady()
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
//賦值給全局變量,注意data為數(shù)據(jù)流
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
//DecodeJob.decodeFromRetrievedData()
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
//DecodeJob.decodeFromData()
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
...
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
...
return result;
} finally {
fetcher.cleanup();
}
}
//DecodeJob.decodeFromFetcher()
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
我們清楚的知道了數(shù)據(jù)流返回后通過cb.onDataFetcherReady()回調(diào)到了DecodeJob類中筛婉。
然后他們的調(diào)用鏈?zhǔn)沁@樣的簇爆。
DecodeJob.onDataFetcherReady()
->DecodeJob.decodeFromRetrievedData()
-->DecodeJob.decodeFromData()
--->DecodeJob.decodeFromFetcher()
---->DecodeHelper.getLoadPath((Class<Data>) data.getClass())
fu*k......怎么又調(diào)用到DecodeHelper中去了?目前從DecodeJob中還啥也看不出來...
向Glide開炮.....
DecodeHelper.getLoadPath()
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
//Registry.getLoadPath()
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
return null;
} else if (result == null) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
// It's possible there is no way to decode or transcode to the desired types from a given
// data class.
if (decodePaths.isEmpty()) {
result = null;
} else {
//利用上述創(chuàng)建的decodePaths 作為參數(shù)創(chuàng)建LoadPath對象并添加到loadPathCache對象中
result = new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
//Registry.getDecodePaths()
@NonNull
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders = decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder = transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(dataClass,registeredResourceClass,registeredTranscodeClass,decoders,transcoder, throwableListPool);
decodePaths.add(path);
}
}
return decodePaths;
}
我們還得梳理下
- 先通過DecodeHelper.getLoadPath()調(diào)用Registry.getLoadPath()然后調(diào)用Registry.getDecodePaths()爽撒。
- 循環(huán)并調(diào)用getDecoders()得到解碼器ResourceDecoder列表入蛆。
- 創(chuàng)建DecodePath對象,并將ResourceDecoder列表對象傳入進(jìn)去硕勿。這個DecodePath就是用來解碼和轉(zhuǎn)換的實體類對象哨毁。
榮耀存于心,而非留于形首尼,汝之赴死挑庶,易如反掌
4.數(shù)據(jù)解碼
上述的數(shù)據(jù)流經(jīng)歷了一系列的準(zhǔn)備,終于要解碼了软能。
上述的DecodeJob.decodeFromFetcher()中還有一個方法叫runLoadPath(data, dataSource, path);
//DecodeJob.runLoadPath()
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
//LoadPath.load()
public Resource<Transcode> load(DataRewinder<Data> rewinder,Options options, int width, int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
//LoadPath.loadWithExceptionList()
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
//循環(huán)拿到DecodePath對象
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
//調(diào)用DecodePath.decode()方法
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
那種勝利在望的感覺終于出現(xiàn)了....
//DecodePath.decode()
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,int width,int height, Options options,
DecodeCallback<ResourceType> callback)throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
@NonNull
private Resource<ResourceType> decodeResource(
DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource<ResourceType> decodeResourceWithList(
DataRewinder<DataType> rewinder,int width, int height,Options options,List<Throwable> exceptions)throws GlideException {
Resource<ResourceType> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
//獲取解碼器迎捺,此時的 ResourceDecoder為StreamBitmapDecoder
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
//等同于調(diào)用StreamBitmapDecoder.decode()
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
...
}
...
return result;
}
//StreamBitmapDecoder.decode()
@Override
//此時的source正式我們請求網(wǎng)絡(luò)回來的InputStream數(shù)據(jù)流
public Resource<Bitmap> decode(InputStream source, int width, int height, Options options) throws IOException {
// 用于修復(fù)標(biāo)記限制,以避免分配適合整個圖像的緩沖區(qū)查排。
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// 用于檢索讀取時引發(fā)的異常凳枝。
//TODO(#126):當(dāng)框架不再返回部分解碼的位圖或提供要確定位圖是否已部分解碼,請考慮刪除跋核。
ExceptionCatchingInputStream exceptionStream =ExceptionCatchingInputStream.obtain(bufferedStream);
//用于讀取數(shù)據(jù)岖瑰。
//確保在讀取圖像標(biāo)題后始終可以重置,以便嘗試解碼完整圖像砂代,即使頭解碼失敗和/或溢出我們的讀取緩沖區(qū)
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
我以為StreamBitmapDecoder終于要解碼了蹋订,可Glide還是比較執(zhí)著。
StreamBitmapDecoder只是將source進(jìn)行了包裝刻伊,包裝成了RecyclableBufferedInputStream對象
最后調(diào)用了 downsampler.decode(invalidatingStream, width, height, options, callbacks);
來 ...
//Downsampler.decode()
public Resource<Bitmap> decode(
InputStream is,
int requestedWidth,
int requestedHeight,
Options options,
DecodeCallbacks callbacks)
throws IOException {
...
byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
bitmapFactoryOptions.inTempStorage = bytesForOptions;
DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE);
DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
boolean isHardwareConfigAllowed =
options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);
try {
Bitmap result =
decodeFromWrappedStreams(
is,
bitmapFactoryOptions,
downsampleStrategy,
decodeFormat,
preferredColorSpace,
isHardwareConfigAllowed,
requestedWidth,
requestedHeight,
fixBitmapToRequestedDimensions,
callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
//Downsampler.decodeFromWrappedStreams()
private Bitmap decodeFromWrappedStreams(
InputStream is,
BitmapFactory.Options options,
DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat,
PreferredColorSpace preferredColorSpace,
boolean isHardwareConfigAllowed,
int requestedWidth,
int requestedHeight,
boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks)
throws IOException {
...
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
...
Bitmap rotated = null;
if (downsampled != null) {
// 如果我們縮放露戒,位圖密度將是我們的目標(biāo)密度椒功。在這里我們把它修正回預(yù)期的密度dpi。
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
//Downsampler.decodeStream()
private static Bitmap decodeStream(
InputStream is,
BitmapFactory.Options options,
DecodeCallbacks callbacks,
BitmapPool bitmapPool)
throws IOException {
if (options.inJustDecodeBounds) {
is.mark(MARK_POSITION);
} else {
// Once we've read the image header, we no longer need to allow the buffer to expand in
// size. To avoid unnecessary allocations reading image data, we fix the mark limit so that it
// is no larger than our current buffer size here. We need to do so immediately before
// decoding the full image to avoid having our mark limit overridden by other calls to
// mark and reset. See issue #225.
callbacks.onObtainBounds();
}
// BitmapFactory.Options out* variables are reset by most calls to decodeStream, successful or
// otherwise, so capture here in case we log below.
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;
String outMimeType = options.outMimeType;
final Bitmap result;
TransformationUtils.getBitmapDrawableLock().lock();
try {
result = BitmapFactory.decodeStream(is, null, options);
} catch (IllegalArgumentException e) {
IOException bitmapAssertionException =
newIoExceptionForInBitmapAssertion(e, sourceWidth, sourceHeight, outMimeType, options);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"Failed to decode with inBitmap, trying again without Bitmap re-use",
bitmapAssertionException);
}
if (options.inBitmap != null) {
try {
is.reset();
bitmapPool.put(options.inBitmap);
options.inBitmap = null;
return decodeStream(is, options, callbacks, bitmapPool);
} catch (IOException resetException) {
throw bitmapAssertionException;
}
}
throw bitmapAssertionException;
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}
if (options.inJustDecodeBounds) {
is.reset();
}
return result;
}
我**智什,終于看到 BitmapFactory.decodeStream() 了动漾,太難了。荠锭。旱眯。
我們還是簡單回顧下
- Downsampler.decode()連帶調(diào)用了Downsampler.decodeFromWrappedStreams()
- 然后調(diào)用了Downsampler.decodeStream()并在該方法中調(diào)用了BitmapFactory.decodeStream()
- 到目前為止數(shù)據(jù)流已經(jīng)解碼完成轉(zhuǎn)換為Bitmap對象。
不要畏懼迷離之道证九,傳統(tǒng)是智慧的糟粕
5.數(shù)據(jù)轉(zhuǎn)換
上述邏輯中已經(jīng)將數(shù)據(jù)流對象轉(zhuǎn)換為了Bitmap對象删豺。但是DecodePath.decode()完成后還需要 transcoder.transcode(transformed, options);
此時的transcoder為BitmapDrawableTranscoder.transcode()
//BitmapDrawableTranscoder.transcode()
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
//BitmapDrawableTranscoder.transcode()
@Nullable
public static Resource<BitmapDrawable> obtain(Resources resources, Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
到這里通過 DecodePath.decode()先獲取到了Bitmap對象。
然后通過transcoder.transcode()將Bitmap包裝成了LazyBitmapDrawableResource對象甫贯。
斷劍重鑄之日吼鳞,騎士歸來之時
6.圖片顯示與那一堆回調(diào)
- 圖片數(shù)據(jù)流獲取到了
- 圖片數(shù)據(jù)流解碼為了Bitmap對象
- 圖片對象轉(zhuǎn)換為了LazyBitmapDrawableResource對象
然后呢?.....該從哪再開始呢叫搁?還沒顯示到頁面呢...
你還記得DecodeJob.decodeFromRetrievedData()方法嗎赔桌?
不記得可以往上翻一下,為了方便渴逻,這里我再復(fù)制一份過來疾党。
//DecodeJob.decodeFromRetrievedData()
private void decodeFromRetrievedData() {
...
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
其實我們上邊分析的這些大部分都是DecodeJob.decodeFromData()旗下的子邏輯。
還有一個方法是 DecodeJob.notifyEncodeAndRelease(resource, currentDataSource);
//DecodeJob.notifyEncodeAndRelease()
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
//是否可以將轉(zhuǎn)換的圖片緩存
if (deferredEncodeManager.hasResourceToEncode()) {
//磁盤緩存的入口
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
//DecodeJob.notifyComplete()
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
//此時的callback為EngineJob惨奕,因為等同于調(diào)用EngineJob.onResourceReady()
callback.onResourceReady(resource, dataSource);
}
//EngineJob.onResourceReady()
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
//EngineJob.notifyCallbacksOfResult()
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
// TODO: 回收并加入緩存雪位。
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
//包裝成EngineResource對象,并沒有其他處理
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
// 在下面的回調(diào)期間保留資源梨撞,這樣我們就不會在通知資源是否由其中一個回調(diào)同步釋放時對其進(jìn)行回收雹洗。
//在這里用一個鎖獲取它,這樣在下面的下一個鎖定部分之前執(zhí)行的任何新添加的回調(diào)在我們調(diào)用回調(diào)之前都不能回收資源卧波。
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
//注意 這個engineJobListener實際上是Engine對象
//內(nèi)部緩存存儲的入口时肿,等同于調(diào)用Engine.onEngineJobComplete()
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
//切主線程顯示圖片
entry.executor.execute(new CallResourceReady(entry.cb));
}
//通知上層刪除弱引用緩存數(shù)據(jù)
decrementPendingCallbacks();
}
//Engine.onEngineJobComplete()
@Override
public synchronized void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// 如果開啟內(nèi)存緩存的話,將解析后的圖片添加到弱引用緩存港粱。
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
//看這里將資源添加到弱引用緩存中螃成。
//key值不重復(fù)返回null,key值重復(fù)返回舊對象
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
//如果key值重復(fù),就將之前的弱引用對象的圖片資源置為null
removed.reset();
}
}
到這該罵娘了查坪,沒事寸宏,該罵罵,罵完咱們繼續(xù).
DecodeJob.notifyEncodeAndRelease(resource, currentDataSource);都看完了到底哪里顯示的圖片呢偿曙?
能不能給我來個痛快?
我們注意EngineJob.notifyCallbacksOfResult()最后有個for循環(huán)
循環(huán)調(diào)用ResourceCallbackAndExecutor.executor.execute(new CallResourceReady(entry.cb))
其實ResourceCallbackAndExecutor.executor是一個線程池
CallResourceReady是個Runnable氮凝,執(zhí)行的正是這個Runnable中的run方法。
在這個CallResourceReady對象中就是圖片顯示的處理邏輯望忆,大哥們我們還得跟進(jìn)...
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
// (b/136032534).
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
}
//EngineJob.callCallbackOnResourceReady()
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
// This is overly broad, some Glide code is actually called here, but it's much
// simpler to encapsulate here than to do so at the actual call point in the
// Request implementation.
//翻山越嶺終于見到你覆醇,這個cb就是SingleRequest
//cb最初是通過SingleRequest.onSizeReady()中的engine.load()傳進(jìn)來的.
//調(diào)用時候傳的是this朵纷,因此此處等于同調(diào)用SingleRequest.onResourceReady()
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
/** A callback method that should never be invoked directly. */
//SingleRequest.onResourceReady()
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
loadStatus = null;
....異常處理
if (!canSetResource()) {
toRelease = resource;
this.resource = null;
//在請求canSetResource()之前炭臭,不能將狀態(tài)設(shè)置為完成永脓。
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
//SingleRequest.onResourceReady()
@GuardedBy("requestLock")
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
//核心1
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
//核心2
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
//核心3
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
//核心4
notifyLoadSuccess();
}
以上邏輯中我們寫了4個注釋,分別和核心1234鞋仍,我們一個一個看下
核心1和核心2
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
其實這個方法往下跟蹤是一個native方法常摧,作用是用來喚醒此對象監(jiān)視器上等待的所有線程。
核心3
//此時的target為ImageViewTarget
target.onResourceReady(result, animation);
//ImageViewTarget.onResourceReady()
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
//ImageViewTarget.setResourceInternal()
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
//設(shè)置動畫的
maybeUpdateAnimatable(resource);
}
//終點站到了威创。
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
看到了么落午,一切的一切都在這句view.setImageBitmap(resource);后結(jié)束了。
我們在看下核心4
notifyLoadSuccess();
private void notifyLoadSuccess() {
if (requestCoordinator != null) {
requestCoordinator.onRequestSuccess(this);
}
}
//主要做清理工作肚豺。
public void onRequestSuccess(Request request) {
synchronized (requestLock) {
if (request.equals(thumb)) {
thumbState = RequestState.SUCCESS;
return;
}
fullState = RequestState.SUCCESS;
if (parent != null) {
parent.onRequestSuccess(this);
}
// Clearing the thumb is not necessarily safe if the thumb is being displayed in the Target,
// as a layer in a cross fade for example. The only way we know the thumb is not being
// displayed and is therefore safe to clear is if the thumb request has not yet completed.
if (!thumbState.isComplete()) {
thumb.clear();
}
}
}
永遠(yuǎn)不要忘記溃斋,吾等為何而戰(zhàn)..
7. 緩存的維護(hù)與流程
但是還有一個疑問沒有解決,那網(wǎng)絡(luò)請求后的數(shù)據(jù)到底緩存到哪里了吸申?
我們先還得看下SingleRequest.onResourceReady()的最后梗劫,在finally中有句 engine.release(toRelease);
//Engine.release(toRelease)
public void release(Resource<?> resource) {
if (resource instanceof EngineResource) {
((EngineResource<?>) resource).release();
} else {
throw new IllegalArgumentException("Cannot release anything but an EngineResource");
}
}
/**
*減少使用包裝資源的使用者數(shù)〗夭辏總機(jī)上必須呼叫線梳侨。
*只有當(dāng)調(diào)用{@link\acquire()}方法的使用者資源用完了
*一般來說,外部用戶不應(yīng)該調(diào)用這個方法框架會幫你處理的日丹。
*/
//EngineResource.release()
void release() {
boolean release = false;
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
release = true;
}
}
if (release) {
listener.onResourceReleased(key, this);
}
}
//Engine.onResourceReleased()
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
//刪除弱引用緩存
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
//看到了么走哺,這里加入了緩存,這cache即是LruResourceCache
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
總結(jié)哲虾,緩存的入口主要體現(xiàn)在3個地方丙躏。
-
內(nèi)部緩存存儲的入口,等同于調(diào)用Engine.onEngineJobComplete()
EngineJob.notifyCallbacksOfResult()
-> engineJobListener.onEngineJobComplete(this, localKey, localResource);
-
內(nèi)部緩存存儲的入口,等同于調(diào)用Engine.onEngineJobComplete()
- 如果開啟內(nèi)存緩存的話束凑,將解析后的圖片添加到弱引用緩存,添加前先封裝成ResourceWeakReference對象晒旅,如果key有重復(fù)的則將之前的弱引用列表的中對應(yīng)的數(shù)據(jù)制空。
Engine.onEngineJobComplete()
-> activeResources.activate(key, resource);
- 如果開啟內(nèi)存緩存的話束凑,將解析后的圖片添加到弱引用緩存,添加前先封裝成ResourceWeakReference對象晒旅,如果key有重復(fù)的則將之前的弱引用列表的中對應(yīng)的數(shù)據(jù)制空。
-
磁盤緩存的入口
DecodeJob.notifyEncodeAndRelease()
->deferredEncodeManager.encode(diskCacheProvider, options);
-
磁盤緩存的入口
如果LruCache有不懂得可以看我的另一個系列的文章 LruCache緩存機(jī)制湘今,深入淺出敢朱,發(fā)現(xiàn)了一個源碼bug
我們最后看下緩存的整體使用流程圖
至此,完整的兩篇Glide講解已經(jīng)完結(jié)摩瞎,看源碼的過程很痛苦拴签,但分析清整個脈絡(luò)很清奇。
我們的日常工作大都是做著重復(fù)的勞動旗们,成長的機(jī)會少之又少蚓哩,無輪什么時候也不要忘了那顆赤誠學(xué)習(xí)的心。
如果本文給你帶來了一點點幫助麻煩給個贊鼓勵一下上渴,同時如果文中有任何錯誤歡迎指出留言岸梨。
謝謝大家...