Glide源码剖析系列
- Android Glide源码剖析系列(一)图片加载请求如何感知组件生命周期
- Android Glide源码剖析系列(二)Glide如何管理图片加载请求
- Android Glide源码剖析系列(三)深入理解Glide图片加载流程
- Android Glide源码剖析系列(四)缓存机制及其原理
为什么选择Glide?
- 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
- 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)Glide可以感知调用页面的生命周期,这就是优势
- 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
- 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
小结:支持图片格式多;Bitmap复用和主动回收;生命周期感应;优秀的缓存策略;加载速度快(Bitmap默认格式RGB565)
Glide简单使用
Glide.with(this)
.load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
.error(R.drawable.aaa)
.placeholder(R.drawable.ic_android_black_24dp)
.fallback(R.drawable.aaa)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(imageView);
源码分析
上篇文章学习了RequestTracker 如何管理图片加载请求,本文开始分析图片加载请求执行流程。
#RequestTracker.java
/** Starts tracking the given request. 把请求添加到requests和pendingRequests*/
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin(); //图片加载请求执行入口
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
request.begin()
方法是请求开始执行的入口,这里的request是SingleRequest类型,所以有必要先搞懂SingleRequest类
/**
* 定义:把资源加载到目标的请求
*/
public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {
//请求的状态
private enum Status {
/** Created but not yet running. */
PENDING,
/** In the process of fetching media. */
RUNNING,
/** Waiting for a callback given to the Target to be called to determine target dimensions. */
WAITING_FOR_SIZE,
/** Finished loading media successfully. */
COMPLETE,
/** Failed to load media, may be restarted. */
FAILED,
/** Cleared by the user with a placeholder set, may be restarted. */
CLEARED,
}
/* 同步锁对象:给请求的操作方法上锁,保证线程安全 */
private final Object requestLock;
//监听请求的状态变化
@Nullable private final RequestListener<R> targetListener;
//协调一个target上的多个请求
private final RequestCoordinator requestCoordinator;
private final Context context;
private final GlideContext glideContext;
@Nullable private final Object model;
private final Class<R> transcodeClass;
//请求的配置参数
private final BaseRequestOptions<?> requestOptions;
private final int overrideWidth;
private final int overrideHeight;
private final Priority priority;
private final Target<R> target;
@Nullable private final List<RequestListener<R>> requestListeners;
private final TransitionFactory<? super R> animationFactory;
private final Executor callbackExecutor;
@GuardedBy("requestLock")
private Resource<R> resource;
@GuardedBy("requestLock")
private Engine.LoadStatus loadStatus;
@GuardedBy("requestLock")
private long startTime;
// Volatile because it's accessed outside of a lock and nullable, even though in practice it will
// always be non-null unless the request is in the object pool.
private volatile Engine engine; //图片处理引擎
/* Variables mutated during a request. */
@GuardedBy("requestLock")
private Status status;
@GuardedBy("requestLock")
@Nullable
private Drawable errorDrawable; //加载失败显示图
@GuardedBy("requestLock")
@Nullable
private Drawable placeholderDrawable; //占位图
@GuardedBy("requestLock")
@Nullable
private Drawable fallbackDrawable;
@GuardedBy("requestLock")
private int width;
@GuardedBy("requestLock")
private int height;
@Override
public void begin() {
synchronized (requestLock) { //加锁
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) { //注释1
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) { //注释2
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) { //注释3
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
experimentalNotifyRequestStarted(model);
cookie = GlideTrace.beginSectionAsync(TAG);
status = Status.WAITING_FOR_SIZE; //状态设为:等待target确定尺寸
if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //注释4
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) { //target设置占位图
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
}
}
逐个分析Request#begin()方法中的if判断语句
-
注释1:如果资源数据为null,执行
onLoadFailed(new GlideException("Received null model"), logLevel)
方法;
private void onLoadFailed(GlideException e, int maxLogLevel) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) { //加锁
loadStatus = null;
status = Status.FAILED; //1.设置请求的状态
isCallingCallbacks = true;
try {
// TODO: what if this is a thumbnail request?
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onLoadFailed(e, model, target, isFirstReadyResource());
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onLoadFailed(e, model, target, isFirstReadyResource());
if (!anyListenerHandledUpdatingTarget) {
setErrorPlaceholder(); //2.如果不是缩略图请求,设置显示失败占位图
}
} finally {
isCallingCallbacks = false;
}
notifyLoadFailed();
}
}
@GuardedBy("requestLock")
private void setErrorPlaceholder() {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = null;
if (model == null) {
error = getFallbackDrawable();
}
// Either the model isn't null, or there was no fallback drawable set.
if (error == null) {
error = getErrorDrawable();
}
// The model isn't null, no fallback drawable was set or no error drawable was set.
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(error); //3.告诉target资源加载失败,并把错误占位图资源回传给target
}
- 设置请求的状态为Status.FAILED;
- 如果不是缩略图请求,setErrorPlaceholder()设置显示失败占位图;
- 告诉target资源加载失败,并把错误占位图资源回传给target
-
注释2:请求状态为RUNNING,不允许执行
begin()
方法 -
注释3:请求状态为COMPLETE,执行
onResourceReady( resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
@Override
public void onResourceReady(
Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
loadStatus = null;
if (resource == null) { //资源为null,回调onLoadFailed
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())) {
toRelease = resource;
this.resource = null;
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()) {
toRelease = resource; //准备回收resource
this.resource = null;
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
GlideTrace.endSectionAsync(TAG, cookie);
return;
}
onResourceReady(
(Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
@GuardedBy("requestLock")
private boolean canSetResource() {
return requestCoordinator == null || requestCoordinator.canSetImage(this);
}
- 如果 resource 或 resource.get() 为空,调用 onLoadFailed(exception);
- 如果不允许设置resource,请求状态设为COMPLETE,并准备回收resource
- 执行
onResourceReady( (Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
@GuardedBy("requestLock")
private void onResourceReady(
Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
// 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 |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
GlideTrace.endSectionAsync(TAG, cookie);
}
资源加载成功回调target.onResourceReady()方法
-
注释4:如果通过RequestOption设置的宽高都大于0或者与Target原始宽高相等,则调用
onSizeReady(overrideWidth, overrideHeight);
;否则重新计算target尺寸,计算完成后依然会调用onSizeReady()方法
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING; //请求的状态设为RUNNING
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
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,
callbackExecutor);
// 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;
}
}
}
- 请求的状态设为RUNNING
- 图片加载引擎Engine开始加载图片
到此为止,Request的begin()方法分析完毕,图片加载流程最终交给Engine执行。Engine是整个图片加载流程中一个非常重要的角色,接下来继续阅读源码揭开Engine的神秘面纱。
#Engine.java
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;
//使用request配置信息构建EngineKey,用于资源缓存
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //从内存缓存中查找资源
if (memoryResource == null) { //缓存中查找不到资源,重用或新建一个新EngineJob
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key); //正在使用的资源列表中查找
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
EngineResource<?> cached = loadFromCache(key); //内存缓存中查找资源
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
- 使用request配置信息构建EngineKey,用作资源缓存时的key
- 从正在使用的资源列表和内存缓存中查找资源
- 如果没找到资源,重用或新建一个新EngineJob
注:Glide著名的图片缓存分为内存缓存和磁盘缓存,这里已经出现了内存缓存,磁盘缓存会在接下来的DecodeJob中处理。关于图片缓存机制会专门撸一篇文章,所以本文中会略过缓存处理相关逻辑。
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<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);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
从EngineJob缓存列表中查找是否有可重用的EngineJob,如果有直接重用;否则新建一个EngineJob,并开启该JobengineJob.start(decodeJob);
#EngineJob.java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
解码工作是耗时操作,不能在主线程操作,因此把decodeJob提交到线程池执行
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback, Runnable,
Comparable<DecodeJob<?>>, Poolable {
@Override
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
// 异常处理
} finally {
//清理工作
}
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE); //1
currentGenerator = getNextGenerator(); //2
runGenerators(); //3
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
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);
}
}
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); //使用modelLoader和model获取原始资源的生成器
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
- 计算nextStage
- 根据nextStage获取对应的Generator
- 执行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循环语句:图片可能来自于磁盘缓存,也可能需要开启任务去获取。我们分析的是没有缓存的情况,所以要先确定使用哪种Generator来生成资源,然后继续查看加载流程,此处以SourceGenerator为例:
继续分析sourceGenerator.startNext()
:
#SourceGenerator.java
@Override
public boolean startNext() {
//跳过缓存相关逻辑,分析没有缓存时的加载流程
while (!started && hasNextModelLoader()) { //注释5
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
startNextLoad(loadData);
}
}
return started;
}
我们知道资源加载由ModelLoader才能完成,先分析一下ModelLoader类
public interface ModelLoader<Model, Data> {
class LoadData<Data> {
public final Key sourceKey; //用于缓存的key
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher; //数据抓取器
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);
}
}
@Nullable
LoadData<Data> buildLoadData(
@NonNull Model model, int width, int height, @NonNull Options options);
boolean handles(@NonNull Model model);
}
原来真正干活的是内部类LoadData,其中的sourceKey用于缓存资源,fetcher是数据抓取器。
回到sourceGenerator.startNext()
注释5循环语句:
#DecodeHelper.java
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;
}
根据model类型从ModelLoader注册列表中筛选出符合要求的ModelLoader列表,最终筛选出能干活的LoadData去执行startNextLoad(loadData)
#SourceGenerator.java
private void startNextLoad(final LoadData<?> toStart) {
//注释6:加载资源
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
注释6:最终加载工作交由Fetcher处理,由于我们传入的model是String类型的图片地址,所以这里干活的是HttpUrlFetcher,进入HttpUrlFetcher#loadData():
#HttpUrlFetcher.java
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
} finally {
}
}
使用loadDataWithRedirects()方法获取网络资源的InputStream,并且把结果回调给callback.onDataReady(result)方法,代码执行到注释6处的onDataReady()回调方法,内部调用onDataReadyInternal(toStart, data)
继续处理inputStream
#SourceGenerator.java
@Synthetic
void onDataReadyInternal(LoadData<?> loadData, 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);
}
}
由于我们是禁用缓存的,直接进入到else语句,这里又是一个方法回调,这个回调方法在DecodeJob中实现
#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;
this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
这里需要经历一系列的方法调用来处理数据:decodeFromRetrievedData() -> decodeFromData(currentFetcher, currentData, currentDataSource) -> decodeFromFetcher(data, dataSource)
#DecodeJob.java
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); //根据data类型获取LoadPath
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();
}
}
#LoadPath.java
public Resource<Transcode> load(
DataRewinder<Data> rewinder,
@NonNull 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);
}
}
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<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
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.java
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);
}
目标资源经过一系列解码(decode)和转换(transform)操作,最终得到我们需要的BitmapDrawableResource并一层层返回给调用发起的地方,也就是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, isLoadingFromAlternateCacheKey);
} else {
runGenerators();
}
}
把decode得到的结果resource传入notifyEncodeAndRelease()方法
private void notifyEncodeAndRelease(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
try {
if (resource instanceof Initializable) {
((Initializable) resource).initialize(); //1
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey); //2
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();
} finally {
GlideTrace.endSection();
}
}
- 执行绘制Bitmap的准备工作
@Override
public void initialize() {
drawable.getBitmap().prepareToDraw();
}
- 图片资源decode完毕,可以拿去显示图片啦
跨越千山万水马上到达终点了,怀着激动的心情继续分析代码:
private void notifyComplete(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
}
onResourceReady()方法在EngineJob中实现。继续追踪代码:
#EngineJob.java
@Override
public void onResourceReady(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
this.isLoadedFromAlternateCacheKey = isLoadedFromAlternateCacheKey;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
//我们省略了异常情况的处理代码和一些注释代码
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener); //1
hasResource = true;
copy = cbs.copy(); //2
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
engineJobListener.onEngineJobComplete(this, localKey, localResource); //3
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb)); //4
}
decrementPendingCallbacks();
}
- engineResource 是资源的一个包装类,负责计算资源被引用的次数,次数为0的时候可以回收资源
- copy内部包装的是
Executors.mainThreadExecutor()
主线程池,方便切换到主线程 - EngineJob执行完毕,把加载的资源加入内存缓存并且从EngineJob缓存列表移除当前job
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
- 把任务切换到主线程执行,也就是说之前的操作都是在子线程中处理
@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();
}
}
}
@GuardedBy("this")
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.onResourceReady(engineResource, dataSource, isLoadedFromAlternateCacheKey);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
最终还是回到了SingleRequest的onResourceReady(),也就是文章开头介绍的begin()方法注释3部分,太远了重新贴一下代码吧!
@GuardedBy("requestLock")
private void onResourceReady(
Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
//1 begin
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
//1 end
if (!anyListenerHandledUpdatingTarget) { //2
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
GlideTrace.endSectionAsync(TAG, cookie);
}
- 如果设置了requestListener或targetListener,则调用它们的onResourceReady()回调方法;
- anyListenerHandledUpdatingTarget含义:是否有listener处理,即listener.onResourceReady()方法的返回值。
如果anyListenerHandledUpdatingTarget==false,进入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);
}
}
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);
}
进入DrawableImageViewTarget#setResource(resource)方法:
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
最终资源被成功设置到ImageView上,图片加载流程结束。
图片加载流程图如果有错误或理解不到位的地方,欢迎批评指正。
Tips:整个加载流程的代码调用真的是很复杂,涉及到Callback回调、抽象方法实现、还有参数N次传递后很难定位来源,建议一边调试程序一边跟踪代码,可以直接跳转到执行点。
网友评论