注意:如果发现Glide解析系列的连接访问不了,表示此文章是规划尚未完成的文章
背景
Glide的使用不在本系列的说明范围,如果要想知道其使用方式请到Glide官网查看。
Gilde是一款快速高效的图片加载库。Glide老早就出来了,且其应用范围非常广,为什么它深受各位开发者的青睐?它有什么过人之处?它的框架设计的好还是坏?作为一名渴望进步的开发者来说是有必要进行深入了解的。如果只关注它的使用方式,不了解它的思想原理、精髓,我们只能停留在搬运工的阶层,很难达到牛人的阶层。
我接下来将对Glide进行各个维度的原理解析。本篇文章先从Glide的整体流程说起。
1、入口
一般情况下我们都是通过如下的方式使用Glide进行加载图片的:
GlideApp.with(activity)
.load(myUrl)
.placeholder(placeholder)
.fitCenter()
.into(imageView);
GlideApp是通过APT根据我们自定义的GlideModule动态生成的,其with的实现如下:
@NonNull
public static GlideRequests with(@NonNull Activity activity) {
return (GlideRequests) Glide.with(activity);
}
GlideApp内部调用Glide的with方法,大致看一下GlideApp的所有代码,其内部都是调用Glide的方法实现的,为什么不直接使用Glide而要通过GlideApp调用呢?其实GlideApp可以理解为是Glide的一个包裹类,这样的目的是为了封装、安全性。
2、Glide的with实现
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
Glidewith方法,返回的是RequestManager,而RequestManager是通过getRetriever().get获取的,我们看下getRetriever:
2.1、Glide的getRetriever:
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
//省略...
return Glide.get(context).getRequestManagerRetriever();
}
getRetriever返回的是RequestManagerRetriever,它先获取Glide的单例(Glide的初始化通过Glide.get实现,Glide的初始化解析请看这里Glide解析二:Glide的初始化),再使用Glide单例中的getRequestManagerRetriever返回RequestManagerRetriever,getRequestManagerRetriever用来干嘛的?请查看Glide解析三:Glide是如何感知组件生命周期的
2.1.1、RequestManagerRetriever的get返回RequestManager
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
//如果是在子线程,则绑定Application的生命周期
return get(activity.getApplicationContext());
} else {
//如果是在UI线程,则绑定Activity的生命周期
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
RequestManagerRetriever有几个get,参数分别有context、fragment、activity、view,其思想主要是根据不同的生命周期对象创建一个RequestManagerFragment,根据这个RequestManagerFragment实现自动感知各大组件的生命周期实现。接着创建RequestManager并绑定RequestManagerFragment,RequestManager主要用来管理和发起一个Glide加载图片的请求,因为绑定了RequestManagerFragment所以可以感知各大组件的生命周期,并根据生命周期对Glide加载图片进行restart、或者stop等操作。
具体的请查看Glide解析三:Glide是如何感知组件生命周期的
3、RequestManager的load实现
步骤2中解析到Glide的get返回RequestManager,然后使用ReuqestManager进行load操作,我们看下load的源码:
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
load方法先通过asDrawable方法创建一个RequestBuilder,在调用RequestBuilder的load。as系列操作主要是用于标识将图片解析成Bitmap还是Drawable。
4、RequestBuilder
步骤3中解析了RequestManager的load方法先创建一个RequestBuilder,再调用RequestBuilder的系列方法设置一些配置信息想transform、apply、error、into等,接着RequestBuilder再构建Glide图片加载对象Request,通过Request对象实现图片加载的请求。
4.1、RequestBuilder的load方法
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
}
load方法有多重,可以传入String、Bitmap、Uri、Drawable、file等,作为model,代表图片加载来源的数据模型。String、uri一般是代表从图片来源于网络;Bitmap代表图片来源当前传进来的Bitmap;Drawable代表图片来源于当前传进来的Drawable;file代表图片来源文件。
4.2、RequestBuilder的transition方法
public RequestBuilder<TranscodeType> transition(
@NonNull TransitionOptions<?, ? super TranscodeType> transitionOptions) {
this.transitionOptions = Preconditions.checkNotNull(transitionOptions);
isDefaultTransitionOptionsSet = false;
return this;
}
transition方法只是设置配置glide图片加载之后图片转换器,并标识为不适用默认的转换器
4.3、RequestBuilder的apply方法
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
apply方法配置Glide请求的一些配置选项信息
4.4、RequestBuilder的error方法
public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
this.errorBuilder = errorBuilder;
return this;
}
error方法配置图片加载失败时的处理器
4.5、RequestBuilder的listener方法
public RequestBuilder<TranscodeType> listener(
@Nullable RequestListener<TranscodeType> requestListener) {
this.requestListeners = null;
return addListener(requestListener);
}
public RequestBuilder<TranscodeType> addListener(
@Nullable RequestListener<TranscodeType> requestListener) {
if (requestListener != null) {
if (this.requestListeners == null) {
this.requestListeners = new ArrayList<>();
}
this.requestListeners.add(requestListener);
}
return this;
}
listener内部调用addListener方法,添加图片加载请求状态和结果监听器
4.6、RequestBuilder的into方法
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
//检测是否在UI线程,如果不是在UI线程则抛出异常
Util.assertMainThread();
//如果view为空则抛出隐藏
Preconditions.checkNotNull(view);
//根据ImageView的缩放类型设置对应的glide图片缩放处理器
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
//先构建ViewTarget,调用into的重载方法
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
into传递的是ImageView时,先构建为ViewTarget,再调用into的另一个重载方法,我们看下ViewTarget的构建实现:
//Glide.java
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
//ImageViewTargetFactory.java
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
这里ImageViewTargetFactory根据加载图片加载回来的类型,构建BitmapImageViewTarget或者DrawableImageViewTarget,默认是DrawableImageViewTarget。两者均继承成ImageViewTarget,ImageViewTarget继承自ViewTarget,ViewTarget实现Target接口。ViewTarget主要用来根据图片加载的状态和结果显示对于的图片、动画,根据对应的生命周期对图片加载请求进行暂停、停止、重新加载等逻辑。Glide解析四:为什么用Target而不直接用ImageView
我们接着看into的重载方法:
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
//校验是否在Ui线程,如果不是则抛出异常
Util.assertMainThread();
//校验target是否为空,如果未空则抛出异常
Preconditions.checkNotNull(target);
//如果没有设置图片加载来源,即从哪里加载图片,则抛出异常
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//克隆图片加载配置选项
options = options.autoClone();
//构建图片加载请求对象
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
//如果之前已经存在图片加载请求对象,并且不是请求状态,则发起请求
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
//取消正在等待加载或者回收已经加载的资源
requestManager.clear(target);
//target设置图片加载请求对象
target.setRequest(request);
//发起请求
requestManager.track(target, request);
return target;
}
这里构建的是request对象,如果我们使用时不指明Thumbnail,那么构建的默认是SingleRequest。
我们看下requestManager.track是如何发起请求的:
void track(@NonNull Target<?> target, @NonNull Request request) {
//添加target到追踪列表中
targetTracker.track(target);
//通过runRequest发起请求
requestTracker.runRequest(request);
}
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);
}
}
5、SingleRequest
SingleRequest是Reuqest的实现类,其主要用实现图片加载的逻辑。
步骤4中分析到图片加载的开始是在request的begin方法,我们看下
5.1、SingleRequest的begin实现:
public void begin() {
//如果正在调用回调方法,则调用此方法就会抛出异常
assertNotCallingCallbacks();
//如果已经被回收则抛出异常
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
//如果图片加载来源,即从哪里加载,则回调失败
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
//如果当前状态正处于加载图片的状态,则抛出异常
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
//如果当前状态处于已经加载完成,则回调资源已经加载完毕
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
//标识当前状态为等待获取View的大小信息
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果View大小已经就绪,则调用onSizeReady方法进行图片加载请求
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果view大小未就绪,则先去获取view的大小
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//通知图片开始加载
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
begin的步骤可以归纳如下:
a、如果当前状态是正在加载图片,则抛出异常,结束
b、如果当前状态是已经加载完成,则回调加载图片已就绪方法
c、如果当前View大小已经就绪,则调用onSizeReady方法实现图片加载
d、如果当前View大小为就绪,则等待View大小就绪再回调onSizeReady实现图片加载
e、调用onLoadStarted通知图片开始加载
至于当View的大小在未知的情况下,怎么获取view的大小并回调onSizeReady的,请查看Glide解析四:为什么用Target而不直接用ImageView
5.2、SingleRequest的onSizeReady实现:
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
//如果当前状态不处于等待获取View大小,则返回结束
return;
}
//更改当前状态为正在运行图片加载中
status = Status.RUNNING;
//获取图片大小的乘积系数
float sizeMultiplier = requestOptions.getSizeMultiplier();
//如果图片大小为-1,则返回图片大小为负数,否则=大小*sizeMultiplier
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
//通过图片加载引擎加载图片
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
onSizeReady的核心是调用图片加载引擎engine的load方法加载图片
6、Engine
6.1、Engine.load的实现:
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//构建key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//根据key从活跃的资源列表中获取图片资源对象
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
//如果活跃的图片资源对象存在,则回调资源加载已就绪方法
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//根据key从缓存中加载图片资源对象
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
//如果缓存图片资源对象存在,则回调资源加载已就绪方法
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//根据key从图片加载任务中获取任务对象
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//如果已经有任务正在加载图片资源,则添加回调,等待任务完成之后调度
current.addCallback(cb);
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);
//调用图片加载任务的start方法开始加载图片
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
对于图片加载引擎Engine的load实现可以归纳如下:
a、构建图片加载对应的key
b、如果活跃资源列表中存在要加载的图片资源,则回调资源准备就绪
c、如果缓存列表中存在要加载的图片资源,则回调资源准备就绪
d、如果存在要加载图片对于的图片加载任务,则添加回调给图片加载任务,等待任务加载图片完成调度
e、构建图片加载任务和图片解析任务对象
f、通过图片加载任务加载任务
不知道对于上面的的这几个步骤你是否有疑问,反正我是有的,不是说三层缓存策略吗?一是缓存,二是磁盘,三是网络,这里看上去像只有一和三,二的磁盘缓存呢?难道是在图片加载任务EngineJob或者DecodeJob中?是的,待下文详解。
这里为什么还要有个活跃缓存?请查看Glide解析五:活跃缓存策略
对于缓存策略请看这里Glide解析六:缓存策略
7、EngineJob
7.1、EngineJob的start
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
boolean willDecodeFromCache() {
//获取下一个状态
Stage firstStage = getNextStage(Stage.INITIALIZE);
//如果当前状态为RESOURCE_CACHE 或者DATA_CACHE代表从磁盘缓存中读取
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
//根据当前状态获取下一个状态
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
//当前状态为初始状态——》如果允许从已缓存的资源中读取,返回转换后磁盘缓存状态,否则返回下一个状态
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
//当前状态从转换后磁盘缓存读取——》如果允许从已缓存原始图片磁盘缓存中读取,则返回原始磁盘缓存状态,否则返回下一个状态
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
//当前已缓存原始图片磁盘缓存中读取,如果只从缓存中读取,则返回完成状态,否则返回从网络读取状态
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
//当前状态从网络读取或者已完成状态,则返回已完成状态
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
这段代码可以看到如果允许从磁盘缓存中读取,则先从磁盘缓存中读取,接着是从网络读取。
上面的代码使用GlideExecutor 线程池调度decodeJob线程,使用decodeJob实现从磁盘、数据源、网络中加载图片。这里说明下RESOURCE_CACHE、DATA_CACHE、SOURCE这三种状态的明确意思:
- RESOURCE_CACHE: 从磁盘缓存中读取转换之后的图片,而这里的图片指的是原始图片转换之后的图片,比如从网络下载下来之后,先缓存到磁盘的就是原始的图片,经过对原始图片进行大小、方向等转换之后的图片就是转换之后的图片。
- DATA_CACHE: 从磁盘缓存中读取原始图片,而这个磁盘缓存存的就是原始图片
- SOURCE: 代表从网络上加载的状态
7、DecodeJob
DecodeJob是一个线程对象,其实现了Runnable接口:
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
//...
}
7.1、DecodeJob的run
@Override
public void run() {
//...
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
//已取消,则通知加载失败
notifyFailed();
return;
}
//run的包裹实现
runWrapped();
} catch (Throwable t) {
//...
if (stage != Stage.ENCODE) {
//当前状态不等于解析图片状态,则代表加载失败
throwables.add(t);
//通知加载失败
notifyFailed();
}
if (!isCancelled) {
throw t;
}
} finally {
if (localFetcher != null) {
//清除数据、释放相关资源
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
run接着调用runWrapped方法
7.2、DecodeJob的runWrapped
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
//当前状态是初始化状态
//根据当前状态获取新的状态
stage = getNextStage(Stage.INITIALIZE);
//获取当前状态的执行器
currentGenerator = getNextGenerator();
//运行当前图片加载执行器
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
//当前状态转为从网络加载
//运行当前图片加载执行器
runGenerators();
break;
case DECODE_DATA:
//当前状态为解析图片数据
//从获取的数据源中解析图片
decodeFromRetrievedData();
break;
default:
//未知的状态,抛出异常
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
步骤6.1中知道,INITIALIZE之后的状态就是从磁盘缓存中读取转换后图片的状态,我们看下getNextGenerator的实现:
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);
}
}
创建一个图片加载的执行器之后,接着就是运行执行器,我们看下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.
}
根据上面的代码分析,我们可以知道优先从磁盘缓存中读取转换后图片,我们看下磁盘缓存中读取转换后图片执行器ResourceCacheGenerator
7.2.1、ResourceCacheGenerator的startNext
public boolean startNext() {
//获取磁盘缓存按键列表
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
//如果按键列表为空,代表没有磁盘缓存,返回执行失败
return false;
}
//返回磁盘资源类型列表
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
//如果磁盘资源类型列表为空抛出异常
// TODO(b/73882030): This case gets triggered when it shouldn't. With this assertion it causes
// all loads to fail. Without this assertion it causes loads to miss the disk cache
// unnecessarily
// throw new IllegalStateException(
// "Failed to find any load path from " + helper.getModelClass() + " to "
// + helper.getTranscodeClass());
}
while (modelLoaders == null || !hasNextModelLoader()) {
//如果图片加载器为空或者没有下一个图片加载器
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
//生成新的磁盘缓存key
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
//根据key获取磁盘缓存文件
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
//根据文件获取对应的磁盘缓存图片加载器
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//遍历磁盘缓存图片加载器
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//图片加载器构建图片加载对象
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
//加载图片图片数据
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
第一次从磁盘缓存加载的时候,磁盘缓存为空,所以返回false。至于磁盘缓存怎么加载图片,请查看Glide解析七:磁盘缓存策略
按照上面的分析结果,从磁盘缓存中读取转换后图片状态之后就是从磁盘缓存中读取原始图片状态,对应的执行器是DataCacheGenerator
7.2.2、DataCacheGenerator的startNext
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
//如果图片加载器或者没有下一个图片加载器
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
//获取key
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
//创建原始图片磁盘缓存key
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
//获取磁盘缓存文件
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
//获取磁盘缓存图片加载器列表
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
//遍历图片加载器
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//构建图片加载对象
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
//加载图片
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
与ResourceCacheGenerator一样,开始并不存在磁盘缓存,所以会走到从网络加载的步骤,至于磁盘缓存怎么加载图片,请查看Glide解析七:磁盘缓存策略
7.2.3、从网络中加载图片
步骤7.2知道从网络加载图片使用的执行器是SourceGenerator,我们看下SourceGenerator的startNext:
public boolean startNext() {
if (dataToCache != null) {
//dataToCache是网络图片流,网络图片流不为空
Object data = dataToCache;
dataToCache = null;
//读取图片缓存流,并缓存图片到磁盘中
cacheData(data);
}
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;
//使用网络图片加载器加载图片
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
这里的网络图片加载器,如果不指定默认使用的是HttpUrlFetcher,如果配置了其他的网络图片加载器,比如OkHttpStreamFetcher,则使用配置的网络加载器,我们这里看HttpUrlFetcher的loadData是怎么加载图片的:
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//获取网络图片输入流
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//回调通知网络图片输入流已经就绪
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
//发起http请求获取图片输入流
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 {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See 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.
}
}
//构建http请求连接
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);
//使用post请求方式
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.
//连接http
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)) {
//http请求成功
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
//重定向重新发起请求
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);
}
}
从网络加载图片,主要是根据给定的url,发起http请求,得到网络图片输入流,再回调onDataReady通知网络图片输入流已经准备就绪好,在回调中对图片输入流进行处理。这里有个问题,为什么不直接在网络读取图片输入流之后就对图片进行解析、缓存等处理?这里Glide遵循了设计模式中的单一职责,网络图片加载器只负责网络请求,具体网络响应内容要怎么处理,是其他模块的职责。
接下来我们看下SourceGenerator的onDataReady是怎么处理网络图片输入流的:
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//需要缓存到磁盘
//保存网络图片输入流到dataToCache
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);
}
}
一般情况下默认是缓存到磁盘的,所有我们看cb.reschedule的源码,这个cb就是DecodeJob:
public void reschedule() {
//设置当前的状态为转为网络图片流就绪状态
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
DecodeJob的reschedule接着有回到Engine的reschedule,我们看下Engine的reschedule:
public void reschedule(DecodeJob<?> job) {
// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
// up.
//在ActiveSourceExecutor线程池中运行DecodeJob线程
getActiveSourceExecutor().execute(job);
}
接着又进入了DecodeJob的run方法,根据前面对DecodeJob run方法的分析,它会进入runWrapped的SWITCH_TO_SOURCE_SERVICE分支,SWITCH_TO_SOURCE_SERVICE分支直接调用runGenerators,而当前的图片执行器还是网络执行器SourceGenerator,接着又执行SourceGenerator的startNext方法:
public boolean startNext() {
if (dataToCache != null) {
//dataToCache就是网络请求的网络图片输入流
Object data = dataToCache;
dataToCache = null;
//缓存网络图片输入流到磁盘中
cacheData(data);
}
//执行原始图片磁盘缓存输入流执行器
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
//...
}
绕了一大圈又回到SourceGenerator的startNext,在startNext中调用cacheData对网络图片输入流进行缓存:
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());
//创建原始图片缓存key
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);
}
具体磁盘是怎么缓存的请查看Glide解析七:磁盘缓存策略
我们继续往下看DataCacheGenerator的startNext,前面我们分析过DataCacheGenerator的startNext,其最终根据key的磁盘缓存的文件,再通过图片加载取读取加载图片,使用的图片加载器就是FileLoader,而FileLoader的fetcher对应的是FileFetcher,然后使用FileFetcher的loadData加载图片,我们看下FileFetcher的loadData方法:
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
try {
data = opener.open(file);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(data);
}
这段代码使用了opener的open方法,opener由两个工厂类创建的,一个是FileLoader的静态内部类StreamFactory,另一个是FileLoader的静态内部类FileDescriptorFactory,我们用StreamFactory来分析:
public static class StreamFactory extends Factory<InputStream> {
public StreamFactory() {
super(new FileOpener<InputStream>() {
@Override
public InputStream open(File file) throws FileNotFoundException {
//open方法直接就是创建一个图片文件输入流,并返回
return new FileInputStream(file);
}
@Override
public void close(InputStream inputStream) throws IOException {
inputStream.close();
}
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
});
}
}
由此可知,FildeLoader图片加载器使用FileFetcher的loadData加载图片主要是获得图片的文件输入流,并回调callback的onDataReady方法,根据前面的分析流程此callback就是DataCacheGenerator,我们看DataCacheGenerator的onDataReady方法:
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
DataCacheGenerator的onDataReady又回调SourceGenerator的onDataFetcherReady,我们看下SourceGenerator的onDataFetcherReady:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
// This data fetcher will be loading from a File and provide the wrong data source, so override
// with the data source of the original fetcher
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
SourceGenerator的onDataFetcherReady又回调DecodeJob的onDataFetcherReady,我们接着看DecodeJob的onDataFetcherReady:
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
//保存key
this.currentSourceKey = sourceKey;
//保存当前图片文件输入流
this.currentData = data;
//保存当前图片加载器使用的图片获取实现类FileFetcher
this.currentFetcher = fetcher;
//图片来源,此处值为DataSource.DATA_DISK_CACHE,来自磁盘缓存
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的onDataFetcherReady,如果当前运行线程不等于当前线程,那么重新规划图片加载流程,接着进入DecodeJob的runWrapper方法的DECODE_DATA分支,DEOCDE_DATA分支也是调用decodeFromRetrievedData,所以我们接着看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 {
//调用decodeFromData进行图片解码,并返回图片资源对象
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//如果图片资源对象不为空,则根据解析结果状态进行相应的操作
notifyEncodeAndRelease(resource, currentDataSource);
} else {
//如果图片资源对象为空,代表图片加载失败
runGenerators();
}
}
这里通过decodeFromData方法对图片文件输入流进行图片解析处理,并返回图片资源对象,通过notifyEncodeAndRelease通知图片加载成功或者失败,并释放相关的资源。具体是怎么对图片文件输入流进行解析处理的请查看Glide解析八:图片解析处理
我们接着看notifyEncodeAndRelease方法的实现:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
//...
notifyComplete(result, dataSource);
//...
}
notifyEncodeAndRelease有调用notifyComplete方法:
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
notifyComplete回调callback的onResourceReady通知图片资源已经加载完成就绪好,这个callback就是EngineJob,我们继续看EngineJob的onResourceReady实现:
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
//保存图片资源对象到resource成员变量中
this.resource = resource;
//数据来源保存到dataSource成员变量中
this.dataSource = dataSource;
//发送MSG_COMPLETE切换到主线程处理
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
接着我们看MAIN_THREAD_HANDLER的MSG_COMPLETE分支:
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
MSG_COMPLETE分支有调用EngineJob的handleResultOnMainThread方法:
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
//已取消图片加载
//回收图片资源
resource.recycle();
//释放相关资源
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
//回调列表为空,抛出异常
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
//将图片资源构成成EngineResource对象
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
//通知图片加载任务执行完成
engineResource.acquire();
listener.onEngineJobComplete(this, key, engineResource);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
//调用回调列表,通知图片资源加载完成且已准备就绪
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
//释放资源
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
我们的重点在cb的onResourceReady回调,而cb是Engine的load方法参数,在Engine load方法中创建EngineJob时将回调参数添加到回调列表中,前面分析的时候通过SingleRequest调用Engine的load方法并将callback传给Engine的load方法的,所以最终这个callback是SingleRequest。我们看下SingleRequest的onResourceReady做什么处理的:
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception = new GlideException("Expected to receive a Resource<R> with an "
+ "object of " + transcodeClass + " inside, but instead got null.");
//如果图片资源为空,通知加载失败
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
//请求转换的图片类型与实际解析得到的图片类型不匹配,则通知图片加载失败
releaseResource(resource);
GlideException exception = new GlideException("Expected to receive an object of "
+ transcodeClass + " but instead" + " got "
+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
+ "Resource{" + resource + "}."
+ (received != null ? "" : " " + "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
//不允许设置图片资源的view,释放相关的资源
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
//调用onResourceReady的重载方法
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
SingleRequest的onResourceReady中又接着调用onResourceReady的重载方法:
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
//设置状态为加载图片完成
status = Status.COMPLETE;
this.resource = resource;
if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
+ LogTime.getElapsedMillis(startTime) + " ms");
}
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
//通知图片加载请求监听列表图片加载完成
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
//通知targetListener图片加载完成
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
//通知target(imageview的包裹类)图片加载完成
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
//通知图片加载成功
notifyLoadSuccess();
}
当图片加载完成时,会先调用RequestListener列表通知图片加载完成,接着回调TargetListener通知图片加载完成,最后回调Target通知图片加载完成。在前面分析的时候Target的具体实现类是DrawableImageViewTarget或者BitmapImageViewTarget,默认是DrawableImageViewTarget,这两两者继承自ImageViewTarget。我们进入ImageViewTarget看下其onResourceReady的实现:
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);
}
调用的是setResource方法设置图片资源,而setResource是个抽象方法,具体实现是在具体实现类DrawableImageViewTarget和BitmapImageViewTarget实现,我们看下DrawableImageViewTarget的setResource的实现:
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
最终调用ImageView来设置图片,到这里Glide图片加载的整体流程就分析讲解完,期间涉及到很多点,并没有展开细说,而是规划拆分为各自的点进行讲解。
我们来概括性的梳理下Glide的加载流程:
- 先通过APT在编译器动态生成GlideApp
- 运行期通过GlideApp的with作为入口返回RequestManager
- 使用RequestManager创建一个图片加载请求构造器RequestBuilder
- 通过RequestBuilder设置图片加载和解析时的一些配置信息:比如playholder、error、apply、transform、listener等
- 通过RequestBuilder的into构建具体图片加载请求对象默认是SingleRequest
- SingleRequest等待ImageView测量得到具体大小之后,在通过图片加载引起Engine来加载图片
- Engine图片加载引擎根据四级缓存策略:优先从获取资源缓存列表加载;接着从缓存列表接着;在从磁盘缓存加载;最后是从网络加载
- 最后加载完成先回调RequestListener通知图片加载完成;再回调TargetListener通知图片加载完成;最后才回调Target(ImageView的包裹类)将图片设置给ImageView
从Glide的整体流程分析下来,还是挺复杂的,为什么Glide要这样设计(或者说Glide这样的框架设计有什么巧妙之处)?
以我个人的见解是Glide的这样的框架设计有这样的巧妙之处:
- 使用方便:使用了链式、响应式的思想,给使用者带来了使用上的便捷
- 封装安全性:动态生成GlideApp间接访问Glide
- 模块、职责划分清晰:使用了各种设计模式(单例、工厂、观察者、构建、适配器、状态等)使模块和相应的职责划分比较清晰、耦合度也比较低
- 扩展性强:Glide的扩展性设计的还是很强的,这也是Glide的一个突出的特点,它提供很多可扩展的功能给开发者,比如:自定义图片加载器、自定义图片解析处理、设置缓存大小、设置缓存路径等等
Glide有哪些亮点,为什么很多人喜欢用?
- 使用方便
- 扩展性强
- 四级缓存策略,提供效率和内存利用率
- 自动感应生命周期
- 高性能:运用了各种缓存、复用策略(LruCache、Pool等),减少对象的频繁回收和创建,避免内存抖动、对象频繁回收导致频繁GC引起的卡顿