上篇文章說(shuō)到了RequestBuilder創(chuàng)建了請(qǐng)求,會(huì)調(diào)用RequestManager的track方法,請(qǐng)求的創(chuàng)建的調(diào)用鏈很長(zhǎng)但是跟蹤下去會(huì)發(fā)現(xiàn)最終創(chuàng)建了一個(gè)SingleRequest對(duì)象
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
其中track是將請(qǐng)求保存起來(lái)
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
如果當(dāng)前不是暫停狀態(tài)則調(diào)用begin方法處理請(qǐng)求,如果是暫停狀態(tài)則加入到pendingRequests集合中
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
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());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
根據(jù)請(qǐng)求的狀態(tài)進(jìn)行相應(yīng)的處理
- 如果傳入的路徑為空則拋出異常
- 如果當(dāng)前請(qǐng)求的狀態(tài)使運(yùn)行狀態(tài) 同樣拋出異常,請(qǐng)求不能重復(fù)提交
- 如果當(dāng)前請(qǐng)求的狀態(tài)使完成狀態(tài),則直接返回相應(yīng)的資源
然后將請(qǐng)求狀態(tài)修改為WAITING_FOR_SIZE,以目標(biāo)view的寬高屬性傳入到onSizeReady中,修改狀態(tài)值為RUNNING然后調(diào)用Engine.load對(duì)請(qǐng)求進(jìn)行處理
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
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);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
額外補(bǔ)充一些,SingleRequest實(shí)現(xiàn)了上面的幾個(gè)接口
- SizeReadyCallback 里面只有一個(gè)onSizeReady方法,上面我們已經(jīng)說(shuō)過(guò)了,這個(gè)方法對(duì)于SingleReuqest的意義可以說(shuō)請(qǐng)求的開(kāi)始
- ResourceCallBack 里面有兩個(gè)方法
void onResourceReady(Resource<?> resource, DataSource dataSource) void onLoadFailed(GlideException e)從字面也可以看出是請(qǐng)求成功的回調(diào),拿到資源之后對(duì)資源的解碼都是在這個(gè)方法的邏輯里面
public final class SingleRequest<R> implements Request,
SizeReadyCallback,
ResourceCallback,
FactoryPools.Poolable
來(lái)看load方法
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) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
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);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
緩存
關(guān)于緩存我會(huì)再單獨(dú)開(kāi)一篇來(lái)說(shuō),前幾篇僅僅是以Glie的流程為主線來(lái)說(shuō)的
首先根據(jù)圖片源的路徑指定view的寬高以及配置屬性等得到一個(gè)key值,然后loadFromActiveResources首先去獲取緩存中的圖片資源
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
activeResources中維護(hù)著一個(gè)HashMap,key為前面我們得的key,value為一個(gè)弱引用,里面有我們的圖片資源,通過(guò)get獲取到資源,如果不為空,調(diào)用acquire對(duì)對(duì)象的引用數(shù)量進(jìn)行加一操作,內(nèi)存緩存是通過(guò)計(jì)數(shù)散列算法來(lái)進(jìn)行相應(yīng)的操作的,如果對(duì)象的計(jì)數(shù)樹(shù)為0,則說(shuō)明暫無(wú)其他對(duì)象引用此資源,那么此資源可以被釋放了,如果不為>0 則說(shuō)明還有其他對(duì)象引用此資源,那么就不釋放此資源,這樣的話就可以保證圖片資源在Lru中不存在但是需要引用的情況。最后通過(guò)ResourceCallback.onResourceReady將圖片返回回去,這里的cb即SingleRequest
如果activeResources中沒(méi)有,那么通過(guò)Lru進(jìn)行獲取
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;
}
其中的cached就是我們?cè)贕lideBuilder中進(jìn)行初始化的Lru對(duì)象,下面有源碼這里不再多說(shuō),
GlideBuilder#build():
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
對(duì)于Lru的數(shù)據(jù)獲取是通過(guò)刪除的方式來(lái)獲取,并且如果獲取成功的話會(huì),放入到activiteResources中,這樣的話就可以對(duì)資源再次進(jìn)行緩存了,最后如果成功的話也是通過(guò)ResourceCallback.onResourceReady將圖片返回回去。
最后一種則是通過(guò)Jobs根據(jù)key獲取EngineJob對(duì)象,然后將自身的 EngineResource和DataSource對(duì)象傳入到ResourceCallback中,Jobs中維護(hù)著一個(gè)Map集合,Key為我們圖片的key,value為EngineJob,onlyRetrieveFromCache默認(rèn)為true,可以在RequestQoptions中設(shè)置。Jobs則是在Engine創(chuàng)建的時(shí)候新創(chuàng)建的。如果集合中有EngineJob對(duì)象,那么新建一個(gè)LoadStatus并返回,如果沒(méi)有,那就新建一個(gè)EngineJob對(duì)象以及DecodeJob對(duì)象,并將新建的EngineJob加入大Jobs中,然后返回
最后調(diào)用EngineJob.Start()開(kāi)啟線程獲取圖片
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
首先判斷是否需要磁盤(pán)緩存,是則使用磁盤(pán)線程池,否則使用內(nèi)存緩存線程池
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);
}
}
diskCacheStrategy是在ReuqestOption中設(shè)置的,對(duì)應(yīng)
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
// 是否允許幾碼緩存中的圖片資源
@Override
public boolean decodeCachedResource() {
return true;
}
// 是否允許解碼緩存中的源數(shù)據(jù)
@Override
public boolean decodeCachedData() {
return true;
}
};
當(dāng)然了 不管是磁盤(pán)緩存還是內(nèi)存最終都會(huì)調(diào)用DecodeJob的run(),接著會(huì)調(diào)用runWrapped()
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);
}
}
runReason在初始化DecodeJob的時(shí)候設(shè)置初始為INITIALIZE,getNextStage()上面有說(shuō),最后stage則是設(shè)置為了RESOURCE_CACHE
調(diào)用getNextGenerator()返回了一個(gè)ResourceCacheGenerator對(duì)象,
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);
}
}
注意實(shí)例化ResourceCacheGenerator第二個(gè)參數(shù),是DecodeJob本身,而DecodeJob本身實(shí)現(xiàn)了下面幾個(gè)接口,有個(gè)簡(jiǎn)單的印象就可以了
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable
最后調(diào)用了runGenerators方法
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
while的判斷條件是 請(qǐng)求沒(méi)有被取消并且currentGenterator != null 并且要currentGenterator.startNext()返回false才可以。currentGentGenterator就是上面我們實(shí)例化的那個(gè)ResourceCacheGenerator對(duì)象。
@Override
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
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());
}
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.
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
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;
}
hepler為新建的DecodeHelper上面我們有說(shuō)到,
List<Key> getCacheKeys() {
if (!isCacheKeysSet) {
isCacheKeysSet = true;
cacheKeys.clear();
List<LoadData<?>> loadData = getLoadData();
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = loadData.size(); i < size; i++) {
LoadData<?> data = loadData.get(i);
if (!cacheKeys.contains(data.sourceKey)) {
cacheKeys.add(data.sourceKey);
}
for (int j = 0; j < data.alternateKeys.size(); j++) {
if (!cacheKeys.contains(data.alternateKeys.get(j))) {
cacheKeys.add(data.alternateKeys.get(j));
}
}
}
}
return cacheKeys;
}
首次創(chuàng)建使用isCacheKeysSet為false,進(jìn)入判斷體,首先根據(jù)getLoadData()獲取一個(gè)LoadData的集合。
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
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);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
這個(gè)方法先獲取一個(gè)ModelLoader的集合,然后遍歷集合將其中的ModelLoader取出,并調(diào)用buildLoadData方法創(chuàng)建一個(gè)LoadData對(duì)象 將其加入loadData集合中 并最終返回 model為圖片源地址。
@NonNull
public synchronized <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
List<ModelLoader<A, ?>> filteredLoaders = new ArrayList<>(size);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
getModelLoadersForClass下面關(guān)聯(lián)的代碼邏輯很長(zhǎng),但是都很簡(jiǎn)單這里就不貼不出來(lái),大致作用就是根據(jù)在實(shí)例化Glide對(duì)象的時(shí)候注冊(cè)的ModelLoader對(duì)象集,根據(jù)圖片源的類型篩選出部分ModelLoader對(duì)象集并最終返回
modelLoader.buildLoadData()這個(gè)方法在我們自定義的ModelLoader中也有一個(gè)
@GlideModule
public class OkHttpGlideModule extends AppGlideModule {
private static final String TAG = "通訊顧問(wèn)";
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
Log.i(TAG, "registerComponents: ");
registry.replace(GlideUrl.class,InputStream.class,new OkHttpUrlLoader.Factory(new HTTPSUtils(context).getInstance()));
}
}
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final Call.Factory mClient;
@Nullable
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl glideUrl, int width, int height, @NonNull Options options) {
return new LoadData<>(glideUrl,new OkHttpStreamFetcher(mClient,glideUrl));
}
}
回到getCacheKeys()中,遍歷得到的LoadData對(duì)象集,并將loadData所持有的key加入到cacheKeys中,其中data.alternateKeys為空,為什么來(lái)看loadData的構(gòu)造器
public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,
@NonNull DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
新建的LoadData中,alternateKeys為空,除了BaseGlideUrlLoader這個(gè)類其他的類都是沒(méi)有傳入alternateKeys的。所以這里也就不再多說(shuō)了。sourceKey就是我們傳入的圖片源,所以這里的集合里面也就一個(gè)key值
回到startNext(),得到key之后,然后調(diào)用DecodeHepler.getRegisteredResourceClasses()
List<Class<?>> getRegisteredResourceClasses() {
return glideContext.getRegistry()
.getRegisteredResourceClasses(model.getClass(), resourceClass, transcodeClass);
}
Registry#getRegisteredResourceClasses()
@NonNull
public <Model, TResource, Transcode> List<Class<?>> getRegisteredResourceClasses(
@NonNull Class<Model> modelClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
List<Class<?>> result = modelToResourceClassCache.get(modelClass, resourceClass);
if (result == null) {
result = new ArrayList<>();
List<Class<?>> dataClasses = modelLoaderRegistry.getDataClasses(modelClass);
for (Class<?> dataClass : dataClasses) {
List<? extends Class<?>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<?> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry
.getTranscodeClasses(registeredResourceClass, transcodeClass);
if (!registeredTranscodeClasses.isEmpty() && !result.contains(registeredResourceClass)) {
result.add(registeredResourceClass);
}
}
}
modelToResourceClassCache.put(modelClass, resourceClass,
Collections.unmodifiableList(result));
}
return result;
}
resourceClass代表圖片的源文件類型,transcodeClass代表需要轉(zhuǎn)換為的圖片類型
RequestManager#asBitmap():
public RequestBuilder<Bitmap> asBitmap() {
return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
}
其中as方法的入?yún)itmap.class為源文件的類型,appley代表設(shè)置需要轉(zhuǎn)換為的圖片類型,即transcodeClass
private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock()
public static RequestOptions decodeTypeOf(@NonNull Class<?> resourceClass) {
return new RequestOptions().decode(resourceClass);
}
public RequestOptions decode(@NonNull Class<?> resourceClass) {
if (isAutoCloneEnabled) {
return clone().decode(resourceClass);
}
this.resourceClass = Preconditions.checkNotNull(resourceClass);
fields |= RESOURCE_CLASS;
return selfOrThrowIfLocked();
}
回到getRegisteredResourceClasses(),先從集合中根據(jù)圖片源的類型以及圖片類型獲取,如果成功獲取到直接返回,如果沒(méi)有獲取到則進(jìn)入判斷方法,到此為止我們還不知這里面存的到底是什么,要獲取的是什么
記得我們?cè)诔跏蓟疓lide的時(shí)候,會(huì)使用Registry通過(guò)append注冊(cè)一大堆的亂七八糟的東西,
Registry#append():
@NonNull
public <Model, Data> Registry append(
@NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
ModelLoaerRegistry#append():
public synchronized <Model, Data> void append(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
multiModelLoaderFactory#append():
synchronized <Model, Data> void append(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
add(modelClass, dataClass, factory, /*append=*/ true);
}
private <Model, Data> void add(
@NonNull Class<Model> modelClass,
@NonNull Class<Data> dataClass,
@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,
boolean append) {
Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
entries.add(append ? entries.size() : 0, entry);
}
上面的一長(zhǎng)串調(diào)用鏈的最終形態(tài)就是將傳入的圖片源路徑類型、圖片類型、以及一個(gè)位置的工廠類封裝到一個(gè)Entry中,再將這個(gè)Entry加入到集合中,為什么要說(shuō)這個(gè)呢,因?yàn)檫@里用到了Entries.
Registry#RegisteredResourceClasses():
@NonNull
public <Model, TResource, Transcode> List<Class<?>> getRegisteredResourceClasses(
@NonNull Class<Model> modelClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
List<Class<?>> result = modelToResourceClassCache.get(modelClass, resourceClass);
if (result == null) {
result = new ArrayList<>();
List<Class<?>> dataClasses = modelLoaderRegistry.getDataClasses(modelClass);
for (Class<?> dataClass : dataClasses) {
List<? extends Class<?>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<?> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry
.getTranscodeClasses(registeredResourceClass, transcodeClass);
if (!registeredTranscodeClasses.isEmpty() && !result.contains(registeredResourceClass)) {
result.add(registeredResourceClass);
}
}
}
modelToResourceClassCache.put(modelClass, resourceClass,
Collections.unmodifiableList(result));
}
return result;
}
這里的modelLoaderRegistry.getDataClasses(modelClass)就是根據(jù)遍歷entries集合中的Entry然后對(duì)比找到與傳入相同的modelClass,并返回一個(gè)圖片源類型的集合
ResourceDecoderRegistry#getResourceClasses():
@NonNull
@SuppressWarnings("unchecked")
public synchronized <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T> dataClass,
@NonNull Class<R> resourceClass) {
List<Class<R>> result = new ArrayList<>();
for (String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry<?, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)
&& !result.contains((Class<R>) entry.resourceClass)) {
result.add((Class<R>) entry.resourceClass);
}
}
}
return result;
}
外部for循環(huán)的代碼不用看 看里面的,這里也是遍歷ectries,然后匹配與傳入的圖片原地址相同類型以及原圖片類型相同的Entry,最后返回圖片類型的集合
然后遍歷這個(gè)集合,并根據(jù)源圖片類型與要轉(zhuǎn)換的指定類型為參,獲取到指定的轉(zhuǎn)換類型集合,最后加入到modelToResourceClassCache中,并返回源圖片類型型集合。
回到ResourceCacheGenerator#startNext(),modelLoaders是磁盤(pán)緩存中的ModelLoader對(duì)象,下面代碼下半部分有寫(xiě),如果創(chuàng)建一個(gè)key對(duì)象,如果本地磁盤(pán)中有對(duì)應(yīng)的文件,那么就根據(jù)此文件獲取到modelLoaders,當(dāng)然此時(shí)的modelLoaders == null,transfromation表示對(duì)圖形的變換,然后通過(guò)一些屬性組成一個(gè)key值,并在磁盤(pán)中嘗試獲取
然后下面接著會(huì)遍歷modelLoaders,并通過(guò)遍歷出來(lái)的ModelLoaders調(diào)用其 buildLoadData()方法 生成一個(gè)LoadData對(duì)象 ,然后調(diào)用其成員屬性fetcher的loadData進(jìn)行加載圖片的操作
上面的操作都是基于DataFetcherGenerator的startNext()執(zhí)行的,我們上面的操作都是在ResourceCacheGenerator中執(zhí)行的,這個(gè)類的意義是從緩存中加載數(shù)據(jù),而且是經(jīng)過(guò)處理的圖片資源緩存,如果在此類中沒(méi)有找到對(duì)應(yīng)的資源,會(huì)在startNext()中的第一個(gè)while循環(huán)中返回false,然后回到DecodeJob#runGenerators的判斷語(yǔ)句中,如果返回false會(huì)執(zhí)行方法體內(nèi)的代碼,首先得到stage的下一個(gè)狀態(tài),當(dāng)前狀態(tài)使RESOURCE_CACHE,下一狀態(tài)就是DATA_CACHE,對(duì)應(yīng)的類是DataCacheGenerator,此類的作用是從未經(jīng)過(guò)處理的圖片資源緩存中獲取,邏輯上基本同ResourceCacheGenerator,當(dāng)然如果緩存中也沒(méi)有,也會(huì)返回false繼續(xù)下一次的遍歷,如果設(shè)置了只從緩存中讀取,那么下一狀態(tài)就是結(jié)束狀態(tài)FINISHED,如果不只是緩存中讀取那就開(kāi)始聯(lián)網(wǎng)下載獲取SourceGenerator
關(guān)于buildLoadData,我們?cè)谧远x的ModelLoader中用到了,我們上面說(shuō)過(guò)了 ,而loaddata.fetcher.loadata()如下
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
Request.Builder requestBuilder = new Request.Builder().url(mGlideUrl.toStringUrl());
for (Map.Entry<String, String> headerEntry : mGlideUrl.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
mCallback = callback;
Request request = requestBuilder.build();
mCall = mFactory.newCall(request);
mCall.enqueue(this);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
Log.i(TAG, "onResponse: ");
mResponseBody = response.body();
if (response.isSuccessful()) {
long contentLength = Preconditions.checkNotNull(mResponseBody).contentLength();
mStream = ContentLengthInputStream.obtain(mResponseBody.byteStream(), contentLength);
mCallback.onDataReady(mStream);
} else {
mCallback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
可以看到我們直接調(diào)用了Okhttp進(jìn)行了請(qǐng)求,成功的話會(huì)通過(guò)CallBack.onDataReady()將資源傳送回去.callBack在調(diào)用loadData的時(shí)候傳遞過(guò)來(lái),也就是加載器本身。我們這里以SourceGenerator為例
@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();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
首先獲取到磁盤(pán)加載策略,磁盤(pán)緩存有幾種策略,全部緩存,只緩存原始數(shù)據(jù),只緩存轉(zhuǎn)換完畢的數(shù)據(jù)。這里判斷是否緩存原始數(shù)據(jù),如果緩存則調(diào)用cb.reschedule(),否則調(diào)用cb.onDataFetcherReady,其中cb為DecodeJob
DecodeJob#reschedule():
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
EngineJob#reschedule():
@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);
}
callback在DecodeJob初始化的時(shí)候傳入的,在Engine中初始化的,對(duì)應(yīng)EngineJob,最后調(diào)用的是DecodeJob中的run(),runWarpped(),因?yàn)樾薷牧藃unReason,所以直接調(diào)用runGenerators(),在之后就是進(jìn)入SourceGenerator中的startNext(),此時(shí)的dataToCache不在為空,直接調(diào)用cacheData()
SourceGenerator#cacehData():
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());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished encoding source to cache"
+ ", key: " + originalKey
+ ", data: " + dataToCache
+ ", encoder: " + encoder
+ ", duration: " + LogTime.getElapsedMillis(startTime));
}
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
中間一句helper.getDiskCache().put(originalKey, writer)則是將資源保存到了磁盤(pán)中,并為sourceCacheGenerator重新賦值,然后在runGenerators()繼續(xù)遍歷,調(diào)用DataCacheGenerator中的startNext(),從磁盤(pán)中讀取資源,讀取結(jié)果的處理跟從網(wǎng)絡(luò)獲取成功的處理大致相同
從磁盤(pán)讀取完之后會(huì)調(diào)用DecodeJob.onDataFetcherReady();
@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;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
TraceCompat.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
TraceCompat.endSection();
}
}
}
上面我們儲(chǔ)存資源到磁盤(pán)的時(shí)候進(jìn)行了一次線程切換,所以這里會(huì)調(diào)用callback.reschedule(),注意runReason的值,然后最后又回到了run(),最后調(diào)用decodeFromRetrievedData
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
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();
}
}
decodeFromData就不說(shuō)了,可以簡(jiǎn)單理解為解析圖片
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 {
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();
}
調(diào)用notifyComplete通知圖片獲取完畢
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
EngineJob#onResourceReady():
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
發(fā)送了一條消息,最后到了handleResultOnMainThread():
@Synthetic
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release(false /*isRemovedFromQueue*/);
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 = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(this, key, engineResource);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
前面判斷如果請(qǐng)求取消了,則釋放資源,如果沒(méi)有取消,注意下面,先是調(diào)用engineResource.acquired()對(duì)資源的引用加1,然后下面判斷cbs的數(shù)量,每遍歷一次就計(jì)數(shù)+1,因?yàn)槿绻粋€(gè)界面多個(gè)地方同時(shí)需要使用這張圖片,每調(diào)用一次就會(huì)產(chǎn)生一個(gè)cb,但是最終只有一個(gè)請(qǐng)求執(zhí)行,等到這個(gè)請(qǐng)求完成之后然后共同使用這張圖片,遍歷完之后,在調(diào)用engineResource.release()釋放-1;遍歷的過(guò)程中,每遍歷一次便通過(guò)cb.onResourceReady()將資源傳遞出去 ,這里的cb為SingleRequest
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception = new GlideException("Expected to receive a Resource<R> with an "
+ "object of " + transcodeClass + " inside, but instead got null.");
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
GlideException exception = new GlideException("Expected to receive an object of "
+ transcodeClass + " but instead" + " got "
+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
+ "Resource{" + resource + "}."
+ (received != null ? "" : " " + "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
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;
if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
+ LogTime.getElapsedMillis(startTime) + " ms");
}
isCallingCallbacks = true;
try {
if ((requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
&& (targetListener == null
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
主要看try-cache中的代碼,如果requestListener.onResourceReady()返回了true,或是targetListener.onResourceReady()返回了true,那么target將不會(huì)去設(shè)置圖片,意思也就是Glide將不會(huì)主動(dòng)為我們的ImageView設(shè)置圖片,如果false則可以
Glide.with(activity)
.load(finalPath)
.apply(new RequestOptions().placeholder(loadingResId).error(failResId).override(width, height))
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
if (displayImageListener != null) {
displayImageListener.onSuccess(imageView, finalPath);
}
return false;
}
})
.into(imageView);
OK,到這里Glide的流程就結(jié)束了