今天整篇就圍繞一個數(shù)據(jù)后獲取階段,所謂后獲取指的是數(shù)據(jù)從網(wǎng)絡(luò)請求成功回本地后到轉(zhuǎn)換成所需的數(shù)據(jù)類型過程,總結(jié)出來就是兩個問題:
- Glide的數(shù)據(jù)后獲取階段的流程?
- Glide的編解碼階段流程?
1.數(shù)據(jù)后獲取階段流程
在<Glide緩存>里面提到過,網(wǎng)絡(luò)下載數(shù)據(jù)是在SourceGenerator中,下載成功后會把數(shù)據(jù)存在本地再從本地讀取,再回憶下這個過程。剛開始run1和run2dataToCache和sourceCacheGenerator為空,會走run3獲取數(shù)據(jù),成功請求后回調(diào)run4,給dataToCache賦值,因?yàn)闋顟B(tài)沒變,所以DecodeJob還是會調(diào)用SourceGenerator,現(xiàn)在會走run1,接著往后走。
// SourceGenerator.java
@Override
public boolean startNext() {
// run 1
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
// run 2
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;
// run 3
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// run 4
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
在run1里面會調(diào)用cacheData, 會從DecodeHelper中獲取一個編碼器,把數(shù)據(jù)通過編碼器寫到本地文件中,文件緩存通過DiskCache管理,然后會給sourceCacheGenerator賦值,它其實(shí)是一個DataCacheGenerator類型,負(fù)責(zé)從本地加載數(shù)據(jù)。接著上面就走到run2, 從本地加載數(shù)據(jù)然后返回true.
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);
}
很明顯,接下來執(zhí)行到DataCacheGenerator中的startNext方法,在run1中根據(jù)File這種類型從注冊的ModelRegistry中取出對應(yīng)的ModelLoader,取出來的可能會有多個,然后循環(huán)遍歷這些ModelLoader,在run2中會構(gòu)造出DataFetcher,run3判斷拿到的DataFetcher轉(zhuǎn)換后的數(shù)據(jù)類型是否有對應(yīng)的Decoder,如果有就用這個DataFetcher加載從緩存拿到的cacheFile文件。
// DataCacheGenerator.java
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
// run 1
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
// run 2
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
// run 3
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
看下對應(yīng)的File,會取到哪些ModelLoader,會取到有四個,分別是ByteBufferFileLoader,FileLoader,UnitModelLoader,其中兩個FileLoader分別對應(yīng)InputStream,FileDescriptor。
上面的循環(huán)會先取到第一個ByteBufferFileLoader,然后用ByteBufferFileFetcher去加載數(shù)據(jù),看下這個類,它是ByteBufferFileLoader的內(nèi)部類, 它的DataSource是DataSource.LOCAL,表示從數(shù)據(jù)來源是本地, 取到的數(shù)據(jù)類型是ByteBuffer.class, 在loadData中通過NIO內(nèi)存映射讀入文件。
// ByteBufferFileLoader.java
private static final class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
private final File file;
@Synthetic
@SuppressWarnings("WeakerAccess")
ByteBufferFetcher(File file) {
this.file = file;
}
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result);
}
...
@NonNull
@Override
public Class<ByteBuffer> getDataClass() {
return ByteBuffer.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.LOCAL;
}
}
文件成功映射后回調(diào)到DataCacheGenerator, 在這里是簡單的調(diào)用回調(diào)函數(shù),到這里數(shù)據(jù)來源變成了DataSource.DATA_DISK_CACHE,意思是已經(jīng)做了映射,但是未做修改的原始數(shù)據(jù)。這里的cb是在SourceGenerator中傳過來的。
// DataCacheGenerator.java
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
在SourceGenerator接口中只是簡單的再調(diào)用它上一級傳給它的cb,也就是DecodeJob, 到了這里就是賦值一個關(guān)鍵變量,data就是上面的ByteBufferFileFetcher取到的ByteBuffer數(shù)據(jù),然后會調(diào)用decodeFromRetrievedData進(jìn)行數(shù)據(jù)解碼。
// DecodeJob.java
@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 {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
在decodeFromRetrievedData中會一路調(diào)用,來到decodeFromFetcher, 會根據(jù)Data的類型ByteBuffer從DecodeHelper中獲取LoadPath,然后runLoadPath。
// DecodeJob.java
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);
}
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();
}
}
后面經(jīng)過解碼和轉(zhuǎn)碼得到了數(shù)據(jù),會notifyEncodeAndRelease通知到上層,調(diào)用到notifyComplete回調(diào)到EngineJob:
// DecodeJob.java
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();
}
}
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
...
notifyComplete(result, dataSource);
...
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
在EngineJob中,會給主線程發(fā)送MSG_COMPLETE消息,回調(diào)到主線程執(zhí)行handleResultOnMainThread方法,在run 1中會回調(diào)onEngineJobComplete,其中l(wèi)istener是Engine。接著在run2中通知監(jiān)聽者Request,在Request里面調(diào)用Target的onResourceReady:
// EngineJob.java
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
...
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
void handleResultOnMainThread() {
...
engineResource.acquire();
// run 1
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();
// run 2
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
上面調(diào)到的onEngineJobComplete是在Engine中,這里會先把資源放到active resources這一層內(nèi)存緩存中。注冊一個resourcelistener,在資源釋放的時候回調(diào),把資源從active resource中移除,挪到cache這一層的內(nèi)存緩存中:
// Engine
public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
Util.assertMainThread();
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
Util.assertMainThread();
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
2.LoadPath
所以看下LoadPath的源碼,它負(fù)責(zé)把獲取到的ByteBuffer放到一個個的DecodePath中進(jìn)行解碼轉(zhuǎn)換,有三個泛型,其中Data類型是獲取到的數(shù)據(jù)類型(比如ByteBuffer, InputStream), ResourceType是中間類型(可能有Gif/Bitmap),Transcode就是最后返回給應(yīng)用層的最終類型(可能有Drawable)等
// LoadPath.java
/**
* For a given {@link com.bumptech.glide.load.data.DataFetcher} for a given data class, attempts to
* fetch the data and then run it through one or more
* {@link com.bumptech.glide.load.engine.DecodePath}s.
*
* @param <Data> The type of data that will be fetched.
* @param <ResourceType> The type of intermediate resource that will be decoded within one of the
* {@link com.bumptech.glide.load.engine.DecodePath}s.
* @param <Transcode> The type of resource that will be returned as the result if the load and
* one of the decode paths succeeds.
*/
public class LoadPath<Data, ResourceType, Transcode> {
private final Class<Data> dataClass;
private final Pool<List<Throwable>> listPool;
private final List<? extends DecodePath<Data, ResourceType, Transcode>> decodePaths;
private final String failureMessage;
...
}
知道了什么是LoadPath后再返回到前面的decodeFromFetcher中,看下LoadPath是怎么拿到的,先從緩存取如果沒有會從新構(gòu)造,構(gòu)造需要有一個參數(shù)DecodePath,會從decoderRegistry和transcoderRegistry分別獲取ResourceDecoder和ResourceTranscoder,分別用于解碼和轉(zhuǎn)碼。
// DecodeHelper.java
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
// Registry.java
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull 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);
if (decodePaths.isEmpty()) {
result = null;
} else {
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull 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;
}
看下調(diào)試過程中有哪些具體的ResourceClasses和TranscodeClasses,可以看到ResourceClasses有三個
- GifDrawable
- Bitmap
- BitmapDrawable
TranscodeClasses有一個Drawable,也就是最后返回給應(yīng)用層的會是Drawable.
構(gòu)造LoadPath需要一個很重要的參數(shù)DecodePath,接下來看看它。
3.DecodePath
再看看DecodePath的源碼, 三個泛型和前面的LoadPath是一樣的。解碼的時候回調(diào)用decode方法,這個方法完成下面三件事:
- 調(diào)用
decodeResource完成數(shù)據(jù)的解碼,得到decoded - 解碼完成后調(diào)用回調(diào)的
onResourceDecoded,在回調(diào)里面回做一些比如圖片的變換,我們設(shè)置的transform就是在這里起作用 - 對上面解碼得到的
decoded進(jìn)行轉(zhuǎn)碼,返回轉(zhuǎn)碼成功的資源
public class DecodePath<DataType, ResourceType, Transcode> {
private static final String TAG = "DecodePath";
private final Class<DataType> dataClass;
private final List<? extends ResourceDecoder<DataType, ResourceType>> decoders;
private final ResourceTranscoder<ResourceType, Transcode> transcoder;
private final Pool<List<Throwable>> listPool;
private final String failureMessage;
public DecodePath(Class<DataType> dataClass, Class<ResourceType> resourceClass,
Class<Transcode> transcodeClass,
List<? extends ResourceDecoder<DataType, ResourceType>> decoders,
ResourceTranscoder<ResourceType, Transcode> transcoder, Pool<List<Throwable>> listPool) {
this.dataClass = dataClass;
this.decoders = decoders;
this.transcoder = transcoder;
this.listPool = listPool;
failureMessage = "Failed DecodePath{" + dataClass.getSimpleName() + "->"
+ resourceClass.getSimpleName() + "->" + transcodeClass.getSimpleName() + "}";
}
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull 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, @NonNull 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<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
if (result != null) {
break;
}
}
return result;
}
...
interface DecodeCallback<ResourceType> {
@NonNull
Resource<ResourceType> onResourceDecoded(@NonNull Resource<ResourceType> resource);
}
}
看一個具體的DecodePath的例子,從failureMessage中可以看到三個泛型分別是DirectByteBuffer->Bitmap->Drawable.同時資源也有設(shè)置寬高等屬性。
再返回到前面獲取LoadPath的過程中。
4.解碼
看下最終根據(jù)數(shù)據(jù)類型獲取到的DecodePath有三個:
- dataClass: DirectByteBuffer, decoder: GifDecoder, transcoder: UnitTranscoder
- dataClass: DirectByteBuffer, decoder: ByteBufferBitmapDecoder, transcoder: BitmapDrawableTranscoder
- dataClass: DirectByteBuffer, decoder: BitmapDrawableDecoder, transcoder: UnitTranscoder
很明顯我們這里會是第二個起作用。看下ByteBufferBitmapDecoder中的源碼,通過Downsampler進(jìn)行對ByteBuffer進(jìn)行解碼成Bitmap,Downsampler里面根據(jù)采樣率、圖片大小等構(gòu)造一個Bitmap返回。
/**
* Decodes {@link android.graphics.Bitmap Bitmaps} from {@link java.nio.ByteBuffer ByteBuffers}.
*/
public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
private final Downsampler downsampler;
public ByteBufferBitmapDecoder(Downsampler downsampler) {
this.downsampler = downsampler;
}
@Override
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
return downsampler.handles(source);
}
@Override
public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height,
@NonNull Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
}
拿到Resource<Bitmap>后會通過BitmapDrawableTranscoder進(jìn)行轉(zhuǎn)碼,返回Resource<BitmapDrawable>:
// BitmapDrawableTranscoder.java
/**
* An {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} that converts {@link
* android.graphics.Bitmap}s into {@link android.graphics.drawable.BitmapDrawable}s.
*/
public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {
private final Resources resources;
// Public API.
@SuppressWarnings("unused")
public BitmapDrawableTranscoder(@NonNull Context context) {
this(context.getResources());
}
/**
* @deprecated Use {@link #BitmapDrawableTranscoder(Resources)}, {@code bitmapPool} is unused.
*/
@Deprecated
public BitmapDrawableTranscoder(
@NonNull Resources resources, @SuppressWarnings("unused") BitmapPool bitmapPool) {
this(resources);
}
public BitmapDrawableTranscoder(@NonNull Resources resources) {
this.resources = Preconditions.checkNotNull(resources);
}
@Nullable
@Override
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
}
最后是通過LazyBitmapDrawableResource.obtain返回的數(shù)據(jù), 這個可以實(shí)現(xiàn)懶加載,只有在真正調(diào)用get方法的時候才會分配BitmapDrawable:
// LazyBitmapDrawableResource.java
/**
* Lazily allocates a {@link android.graphics.drawable.BitmapDrawable} from a given
* {@link android.graphics.Bitmap} on the first call to {@link #get()}.
*/
public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,
Initializable {
private final Resources resources;
private final Resource<Bitmap> bitmapResource;
...
@Nullable
public static Resource<BitmapDrawable> obtain(
@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
private LazyBitmapDrawableResource(@NonNull Resources resources,
@NonNull Resource<Bitmap> bitmapResource) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
}
@NonNull
@Override
public Class<BitmapDrawable> getResourceClass() {
return BitmapDrawable.class;
}
@NonNull
@Override
public BitmapDrawable get() {
return new BitmapDrawable(resources, bitmapResource.get());
}
@Override
public int getSize() {
return bitmapResource.getSize();
}
@Override
public void recycle() {
bitmapResource.recycle();
}
@Override
public void initialize() {
if (bitmapResource instanceof Initializable) {
((Initializable) bitmapResource).initialize();
}
}
}
5.總結(jié)
Glide在數(shù)據(jù)請求成功后如果能允許緩存,就會緩存完成后通過讀取本地?cái)?shù)據(jù),然后完成解碼和轉(zhuǎn)碼返回給應(yīng)用層,也是基于門面模式,所以支持類型的轉(zhuǎn)接碼都會在Glide初始化的時候注冊到對應(yīng)的Registry中,數(shù)據(jù)下載成功后根據(jù)數(shù)據(jù)類型從Registry組裝LoadPath和DecodePath,完成解碼轉(zhuǎn)碼工作。