今天我要对着 Glide数据加载 进行一顿暴讲,我要给它讲的 锃光瓦亮,我要给它讲的 乌漆嘛黑。
上回针对Gldie的准备工作进行了详细分解,从使用到原理,从整体到局部整体喷了一遍。
还是不太懂得小伙伴可以飞机直达 对着那Glide最新版本就是一顿暴讲 ,因为之前的处理不太了解的话,会对接下来的神逻辑产生困扰。
当然了,如果此刻确实困了的同学,我一定给你一个 好梦...
以下的几个标题是在我写完整篇后重新总结的,这样会比较有针对性。
- 缓存的读取
- 请求的启动
- 请求成功后的数据处理(InputStream的处理)
- 数据解码
- 数据转换
- 图片显示与那一堆回调
- 缓存的维护与使用流程
来,我们开始搞...
兄台准备好了么?我要以Glide数据加载,祭我大诺克萨斯!
1. 缓存的读取
缓存分为弱引用缓存和LRU缓存。
前景回顾下,我们上文已经分析到了 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()进行初始化的,此时使用的engine即是Glide.with()初始化的对象。
我们跟进看下 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;
//构建唯一key,注意构建过程的入参,包含宽高,地址,等参数。
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass,
transcodeClass, options);
EngineResource<?> memoryResource;
synchronized (this) {
//加锁,从内存中获取,可直接看后续的跟进.
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//如果缓存中都没有找到,则调用waitForExistingOrStartNewJob(),后续单独展开分析。
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 则避免回调,防止调用方死锁。
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
//isMemoryCacheable默认情况下为true
//当配置中设置了RequestOptions.skipMemoryCacheOf()的值的话:
//1.当skipMemoryCacheOf传入true时为false,即关闭内存缓存
//2.当skipMemoryCacheOf传入false时为true,即开启内存缓存
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中获取数据
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) {
//判断当前对象是否被回收,如果没有被回收引用计数+1,如已回收则抛异常。
cached.acquire();
//添加到弱引用Map中,方便下次使用。
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
//虽然调用的是cache.remove(key),但是删除成功后会返回删除的对象,如果没有找到删除对象则返回null
//此时的cache为LruResourceCache,是在Glide初始化时由GlideBuilder.build()时创建的。
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
//这些if判断目前还不是特别清楚逻辑,需要知道cache数据怎么添加的才能清楚这些。
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;
}
虽然现在不清楚缓存的数据在哪个位置添加的,但是我们清楚了缓存的调用顺序,先弱引用,后Lru,最后走网络。
- 首先通过 keyFactory.buildKey() 生成图片的唯一key。
- 其次 loadFromActiveResources() 从activeResources(弱引用Map)中查找对应的资源。
- 最后通过 loadFromCache()->getEngineResourceFromCache()从LruResourceCache删除资源 ,如果删除成功则返回原数据,并将数据添加到activeResources中,同时引用技术器+1。(此处用删除代替查询,一举两得。)
意义与生俱来,我们只需遵其而行,真意,环绕四周。动则得福。
2. 请求的启动
如果弱引用和Lru均没获取到数据需通过 waitForExistingOrStartNewJob()方法 进行处理,我们继续。
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被当作参数传入了 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);
//添加回调和回调线程池。
engineJob.addCallback(cb, callbackExecutor);
//启动decodeJob这个Runnable
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
看到这里感觉懵逼很正常,我看到这也是懵逼的,咱们继续,看看 engineJob.start(decodeJob);干了什么事
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//获取GlideExecutor 线程池
GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
//开始执行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无效,因此在此处设置一个局部变量以确保以任何方式清除该fetcher。
DataFetcher<?> localFetcher = currentFetcher;
try {
//如果状态为已取消的状态则调用notifyFailed(),连带调用callback.onLoadFailed(e);
if (isCancelled) {
notifyFailed();
return;
}
//关键方法
runWrapped();
} catch (CallbackException e) {
// 如果不是由Glide控制的回调抛出异常,我们应该避免下面的Glide特定调试逻辑。
throw e;
} catch (Throwable t) {
// 捕捉可抛出的对象,并不是处理oom的例外。我们在GlideExecutor中使用了.submit(),因此我们不会通过这样做悄悄地隐藏崩溃。
//但是,我们要确保在加载失败时始终通知回调。如果没有这个通知,未经处理的抛出文件永远不会通知相应的回调
//这可能会导致负载永远静默挂起,这种情况对于在后台线程上使用Futures的用户尤其不利。
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG,"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t);
}
// 如果当前状态是encoding,则直接callback.onLoadFailed(e);
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
// 需要关闭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实现接口后的具体逻辑
while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
跟进 SourceGenerator实现后的startNext()
@Override
public boolean startNext() {
//缓存判断的一些逻辑
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
//放入缓存,构建缓存key,存储原图等..这里就不展开分析了
cacheData(data);
}
//当原始图片放入磁盘缓存后,sourceCacheGenerator为DataCacheGenerator
//然后继续执行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()方法中提前进行了创建。
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//注意后续loadData对象实例化的部分。
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
//DecodeHelper.getLoadData()
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//modelLoaders 得到的是一个HttpGlideUrlLoader 对象列表具体可跟进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);
//相当于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,因此缓存它们可以节省一些对象实例化和解析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有关,此时传入的是HttpUrlFetcher。
this.fetcher = Preconditions.checkNotNull(fetcher);
}
还记得上述的SourceGenerator.startNext()方法中的这一句么?
loadData.fetcher.loadData(helper.getPriority(), this);
此时的 loadData.fetcher为HttpUrlFetcher,为啥是HttpUrlFetcher上述的LoadData对象初始化已经说明了。
//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
//因为调用loadData()的地方是在SourceGenerator.startNext()loadData.fetcher.loadData(helper.getPriority(), this)中
//调用时的入参this则为SourceGenerator对象。
callback.onDataReady(result);
} catch (IOException e) {
...
callback.onLoadFailed(e);
} finally {
...
}
}
//我们继续看下,快到头了。
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会执行额外的网络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)) {
//如果返回的状态码是200则直接返回数据流
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);
}
}
至此网络的加载回调的流程结束了,后续就是数据流的处理过程。
但是 DecodeJob.runWrapped() 整个过程有点乱,咱们捋一下。
- 通过RunReason.INITIALIZE判断runWrapped()中的switch走第一个case。
- 调用getNextStage()获得返回值为Stage.SOURCE
- 利用 2的返回值,调用 getNextGenerator()初始化SourceGenerator对象。
- 调用runGenerators()方法连带调用currentGenerator.startNext(),此时的currentGenerator为SourceGenerator对象。
- 在SourceGenerator.startNext()方法中,调用DecodeHelper.getLoadData().get(loadDataListIndex++);此处的DecodeHelper即是在DecodeJob.build()->DecodeJob.init()中初始化的DecodeHelper。
- 在DecodeHelper.getLoadData()中获取到HttpGlideUrlLoader列表对象(通过glideContext.getRegistry().getModelLoaders(model)获取到)。
- 循环HttpGlideUrlLoader列表,调用HttpGlideUrlLoader.buildLoadData()初始化LoadData对象。
- 此时第5步的 helper.getLoadData().get()逻辑走完,继续SourceGenerator.startNext()中的loadData.fetcher.loadData(),loadData.fetcher类型依赖于LoadData初始化,此时的类型是HttpUrlFetcher,也就是HttpUrlFetcher.loadData();
-
- 在HttpUrlFetcher.loadData()中调用loadDataWithRedirects(),然后通过HttpURLConnection请求对应的url地址获取InputStream数据流。
- 9.1请求有两种情况,第一是直接返回状态码200,则直接返回数据流。
- 9.2状态码返回302,通过getHeaderField("Location")获取重定向的URL递归调用loadDataWithRedirects()进行请求。
- 请求完成后,在HttpUrlFetcher.loadData()方法中调用callback.onDataReady(result);此时的callback是loadData()的入参。而HttpUrlFetcher.loadData()是在之前的SourceGenerator.startNext()中调用的,并且传了this,因此callback为SourceGenerator
总结完后是不是清晰了一些,上边这一大堆,最重要的目的就是通过HttpURLConnection获取到图片的数据流,并通过回调返回。
不可久留一处,我遵循此道,直至终结
3.请求成功后的数据处理(InputStream的处理)
至此,我们已经拿到了网络返回的InputStream数据流,接下来我们看下返回的数据流到底咋用的?
//SourceGenerator.onDataReady()
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// 我们可能会接到其他回调。在此之前,我们应该重新切回Glide的线程。
//cb是一个FetcherReadyCallback对象,在SourceGenerator构造函数赋值,初始化在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为数据流
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);
}
我们清楚的知道了数据流返回后通过cb.onDataFetcherReady()回调到了DecodeJob类中。
然后他们的调用链是这样的。
DecodeJob.onDataFetcherReady()
->DecodeJob.decodeFromRetrievedData()
-->DecodeJob.decodeFromData()
--->DecodeJob.decodeFromFetcher()
---->DecodeHelper.getLoadPath((Class<Data>) data.getClass())
fu*k......怎么又调用到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 {
//利用上述创建的decodePaths 作为参数创建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()调用Registry.getLoadPath()然后调用Registry.getDecodePaths()。
- 循环并调用getDecoders()得到解码器ResourceDecoder列表。
- 创建DecodePath对象,并将ResourceDecoder列表对象传入进去。这个DecodePath就是用来解码和转换的实体类对象。
荣耀存于心,而非留于形,汝之赴死,易如反掌
4.数据解码
上述的数据流经历了一系列的准备,终于要解码了。
上述的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++) {
//循环拿到DecodePath对象
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
//调用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;
}
那种胜利在望的感觉终于出现了....
//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();
//等同于调用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正式我们请求网络回来的InputStream数据流
public Resource<Bitmap> decode(InputStream source, int width, int height, Options options) throws IOException {
// 用于修复标记限制,以避免分配适合整个图像的缓冲区。
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// 用于检索读取时引发的异常。
//TODO(#126):当框架不再返回部分解码的位图或提供要确定位图是否已部分解码,请考虑删除。
ExceptionCatchingInputStream exceptionStream =ExceptionCatchingInputStream.obtain(bufferedStream);
//用于读取数据。
//确保在读取图像标题后始终可以重置,以便尝试解码完整图像,即使头解码失败和/或溢出我们的读取缓冲区
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还是比较执着。
StreamBitmapDecoder只是将source进行了包装,包装成了RecyclableBufferedInputStream对象
最后调用了 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) {
// 如果我们缩放,位图密度将是我们的目标密度。在这里我们把它修正回预期的密度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()连带调用了Downsampler.decodeFromWrappedStreams()
- 然后调用了Downsampler.decodeStream()并在该方法中调用了BitmapFactory.decodeStream()
- 到目前为止数据流已经解码完成转换为Bitmap对象。
不要畏惧迷离之道,传统是智慧的糟粕
5.数据转换
上述逻辑中已经将数据流对象转换为了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.图片显示与那一堆回调
- 图片数据流获取到了
- 图片数据流解码为了Bitmap对象
- 图片对象转换为了LazyBitmapDrawableResource对象
然后呢?.....该从哪再开始呢?还没显示到页面呢...
你还记得DecodeJob.decodeFromRetrievedData()方法吗?
不记得可以往上翻一下,为了方便,这里我再复制一份过来。
//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 {
//是否可以将转换的图片缓存
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,因为等同于调用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);
// 在下面的回调期间保留资源,这样我们就不会在通知资源是否由其中一个回调同步释放时对其进行回收。
//在这里用一个锁获取它,这样在下面的下一个锁定部分之前执行的任何新添加的回调在我们调用回调之前都不能回收资源。
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
//注意 这个engineJobListener实际上是Engine对象
//内部缓存存储的入口,等同于调用Engine.onEngineJobComplete()
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
//切主线程显示图片
entry.executor.execute(new CallResourceReady(entry.cb));
}
//通知上层删除弱引用缓存数据
decrementPendingCallbacks();
}
//Engine.onEngineJobComplete()
@Override
public synchronized void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// 如果开启内存缓存的话,将解析后的图片添加到弱引用缓存。
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值不重复返回null,key值重复返回旧对象
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
//如果key值重复,就将之前的弱引用对象的图片资源置为null
removed.reset();
}
}
到这该骂娘了,没事,该骂骂,骂完咱们继续.
DecodeJob.notifyEncodeAndRelease(resource, currentDataSource);都看完了到底哪里显示的图片呢?
能不能给我来个痛快?
我们注意EngineJob.notifyCallbacksOfResult()最后有个for循环
循环调用ResourceCallbackAndExecutor.executor.execute(new CallResourceReady(entry.cb))
其实ResourceCallbackAndExecutor.executor是一个线程池
CallResourceReady是个Runnable,执行的正是这个Runnable中的run方法。
在这个CallResourceReady对象中就是图片显示的处理逻辑,大哥们我们还得跟进...
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()传进来的.
//调用时候传的是this,因此此处等于同调用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()之前,不能将状态设置为完成。
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方法,作用是用来唤醒此对象监视器上等待的所有线程。
核心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);
//设置动画的
maybeUpdateAnimatable(resource);
}
//终点站到了。
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
看到了么,一切的一切都在这句view.setImageBitmap(resource);后结束了。
我们在看下核心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();
}
}
}
永远不要忘记,吾等为何而战..
7. 缓存的维护与流程
但是还有一个疑问没有解决,那网络请求后的数据到底缓存到哪里了?
我们先还得看下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");
}
}
/**
*减少使用包装资源的使用者数。总机上必须呼叫线。
*只有当调用{@link\acquire()}方法的使用者资源用完了
*一般来说,外部用户不应该调用这个方法框架会帮你处理的。
*/
//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);
}
}
总结,缓存的入口主要体现在3个地方。
-
内部缓存存储的入口,等同于调用Engine.onEngineJobComplete()
EngineJob.notifyCallbacksOfResult()
-> engineJobListener.onEngineJobComplete(this, localKey, localResource);
-
内部缓存存储的入口,等同于调用Engine.onEngineJobComplete()
- 如果开启内存缓存的话,将解析后的图片添加到弱引用缓存,添加前先封装成ResourceWeakReference对象,如果key有重复的则将之前的弱引用列表的中对应的数据制空。
Engine.onEngineJobComplete()
-> activeResources.activate(key, resource);
- 如果开启内存缓存的话,将解析后的图片添加到弱引用缓存,添加前先封装成ResourceWeakReference对象,如果key有重复的则将之前的弱引用列表的中对应的数据制空。
-
磁盘缓存的入口
DecodeJob.notifyEncodeAndRelease()
->deferredEncodeManager.encode(diskCacheProvider, options);
-
磁盘缓存的入口
如果LruCache有不懂得可以看我的另一个系列的文章 LruCache缓存机制,深入浅出,发现了一个源码bug
我们最后看下缓存的整体使用流程图
至此,完整的两篇Glide讲解已经完结,看源码的过程很痛苦,但分析清整个脉络很清奇。
我们的日常工作大都是做着重复的劳动,成长的机会少之又少,无轮什么时候也不要忘了那颗赤诚学习的心。
如果本文给你带来了一点点帮助麻烦给个赞鼓励一下,同时如果文中有任何错误欢迎指出留言。
谢谢大家...