Glide的图片加载流程
上一章节Android图片加载框架Glide源码解析(一)中讲Glide的基本使用,对于不会使用的建议先去看下。
体验了Glide的便捷及强大后,我们一定很好奇Glide是如何加载图片的或者说加载流程又是怎样的呢?不用急,本章我们就从源码上一步步分析加载流程。
1、Glide一次调用的代码如下:
Glide.with(context)
.load(url)
.apply(RequestOptions.placeholderOf(R.drawable.ic_default)
.error(R.drawable.ic_error))
.thumbnail(0.4f)
.into(imageview);
2、从调用流程入手进行分析
1.调用with()静态方法
Glide类本身是一个单例,调用静态方法with()去实例化Glide类并返回RequestManager对象,以供后面使用。静态方法with()是一个重载方法,参数可以是Context、Activity、Fragment、View、FragmentActivity这五种,源码如下:
/**
* 传入对象是Context,
*/
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
//传入对象是Activity
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
//传入对象是FragmentActivity
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
//传入对象是Fragment
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
//传入对象是View
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
从源码可以看到,这几个重载方法内部均调用了同一个静态方法getRetriever(),那么这个方法是干嘛用的呢,看源码:
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
从源码我们可以看到,先去对参数context进行非空检查,然后调用Glide的静态方法get()采用懒汉式的方式实现单例,通过Builder模式实例化Glide对象,由于篇幅这里就不贴初始化的代码了。初始化后调用getRequestManagerRetriever()得到RequestManagerRetriever对象,然后调用get()就能获得一个RequestManager对象。
2.通过RequestManager类管理Request请求
RequestManager类是实现Glide加载资源的管理类,根据Activity生命周期管控资源加载请求,可以开启、暂停及重启。
通过调用load()重载方法创建一个RequestBuilder,去加载资源。
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
其中,asDrawable方法主要是设置加载的资源最终以Drawable对象返回,默认情况下,Glide加载图片资源时以Drawable对象对象,目前Glide支持返回Drawable、Bitmap、gif和File四种,在不想使用默认的drawable返回的情况下可以在调用load前调用对应的方法,如Bitmap对应asBitmap();
Glide.with(context)
.asBitmap() //该句表示加载资源后保存为Bitmap对象
.load(url)
.into(imageview);
注:如果调用asBitmap()后在调用load方法,此时调用的是RequestBuilder类的load方法,注意区分!!!
好了,回到正题,上述调用load后内部是通过asDrawable()设置资源返回类型,并返回一个RequestBuilder对象,然后调用RequestBuilder的方法load(),看一下源码:
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
可以看到这里new了一个requestBuilder对象。
3.RequestOptions实现各种选项设置
通过上述返回requestBuilder对象后,后续的加载资源及一些设置就在requestBuilder类中实现了。在V4版本中,Glide将一些选项设置模块划分出来,放在RequestOptions类中,其中包括设置占位图、错误图、缓存机制、关闭动画、scaleType等。在调用apply()方法时传入RequestOptions对象,apply()方法源码如下:
//传入RequestOptions类对象,并保存起来
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}
//判断默认选项和设置的是否一致,如果一致返回this.requestOptions的一个克隆,如果不一致则直接返回 this.requestOptions
protected RequestOptions getMutableOptions() {
return defaultRequestOptions == this.requestOptions
? this.requestOptions.clone() : this.requestOptions;
}
获取到当前的RequestOptions对象后,通过调用对象方法的apply方法,将上传入的requestOptions对象传入并保持设置,方便后续使用,同时返回了新的RequestOptions对象赋值给this.requestOptions变量,下面是RequestOptions类中的apply()方法:
public RequestOptions apply(@NonNull RequestOptions other) {
//允许自动克隆,则直接克隆一个RequestOptions对象然后设置类各种属性,并直接返回该对象
if (isAutoCloneEnabled) {
return clone().apply(other);
}
...
中间省略代码主要用于判断是否设置了对应的属性(占位图、错误、缓存等选项),没设置过就去设置,由于代码量大,这里不贴出了
...
// Applying options with dontTransform() is expected to clear our transformations.
//这里检查是否要动画效果,如果不需要,就清除所有过场动画效果
if (!isTransformationAllowed) {
transformations.clear();
fields &= ~TRANSFORMATION;
isTransformationRequired = false;
fields &= ~TRANSFORMATION_REQUIRED;
isScaleOnlyOrNoTransform = true;
}
fields |= other.fields;
options.putAll(other.options);
return selfOrThrowIfLocked();
}
Glide加载图片资源中的一些选项设置都在这个RequestOptions类中,该类也提供了一些选项设置的静态方法,设置完成直接返回requestOptions对象,不需要用户自己去new。
4.调用RequestBuilder类中into()方法实现资源加载并设置到目标target中
前面几点讲述了资源加载之前的基础设置及选项设置,最后调用into实现资源的加载。into()方法时个重载类,最终几个方法都会进入到into(Y target,RequestListener<TranscodeType> targetListener,RequestOptions options)中,不废话,直接看源码:
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();//检测是否在主线程,非主线程调用时抛异常
Preconditions.checkNotNull(target);//判断target非空
//防止在load之前调用into方法
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
//通过buildRequest()方法构建一个Request对象
Request request = buildRequest(target, targetListener, options);
//获取target之前的request请求对象
Request previous = target.getRequest();
//将新构建的request对象与旧的previous对象进行比较,判断是否相同。
// isSkipMemoryCacheWithCompletePreviousRequest用于判断options是否有使用缓存机制和previous是否已经加载完毕
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
//判断previous是否正在运行,如果没在运行,则执行previous这个请求
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
into方法中主要做了以下两个方面的事情:
通过buildRequest()方法构建一个Request;
-
获取target的Request对象,与构建的Request对象进行比较,并检测是否采用缓存及target的上次请求是否已经完成:
1.两个对象相等,并且options采用了缓存或target的上次请求未完成,则将构建的request进行回收,并且 判断target的request是否正在运行,正在运行则直接返回target,若为正在运行则调用Request的begin方法 启动同步加载资源请求;
2.两个对象不等,或者option没使用缓存机制和target的请求已经完成,清除target之前的设置,并将新构建的request对象设置给target,通过track方法启动Request请求;
buildRequest()方法源码://构建一个Request对象 private Request buildRequest(Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener,RequestOptions requestOptions) { return buildRequestRecursive(target,targetListener, /*parentCoordinator=*/ null, transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight(), requestOptions); } /** * 主要做的事情就是先创建一个主加载资源的Request对象,然后根据是否设置了加载错误时 * 显示资源选项,创建一个errorRequest加载资源对象,最后返回Request **/ private Request buildRequestRecursive(Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, RequestOptions requestOptions) { // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator. ErrorRequestCoordinator errorRequestCoordinator = null; if (errorBuilder != null) { errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator); parentCoordinator = errorRequestCoordinator; } //创建加载资源的Request请求 Request mainRequest = buildThumbnailRequestRecursive( target, targetListener, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, requestOptions); if (errorRequestCoordinator == null) { return mainRequest; } int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth(); int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight(); if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.requestOptions.isValidOverride()) { errorOverrideWidth = requestOptions.getOverrideWidth(); errorOverrideHeight = requestOptions.getOverrideHeight(); } Request errorRequest = errorBuilder.buildRequestRecursive( target, targetListener, errorRequestCoordinator, errorBuilder.transitionOptions, errorBuilder.requestOptions.getPriority(), errorOverrideWidth, errorOverrideHeight, errorBuilder.requestOptions); errorRequestCoordinator.setRequests(mainRequest, errorRequest); return errorRequestCoordinator; }
isSkipMemoryCacheWithCompletePreviousRequest方法源码:
private boolean isSkipMemoryCacheWithCompletePreviousRequest( RequestOptions options, Request previous) {
return !options.isMemoryCacheable() && previous.isComplete();
}
当条件不满足时,先执行clear,在执行track方法,实现Request请求,源码如下:
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
该方法,将target加入到targetTracker列表中,然后运行Request,看下runRequest方法:
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);
}
}
代码很简单,一看就懂。
到目前为止一次Glide加载图片资源的流程已经完成大半了,后面就是看Request如何加载资源并显示到target的。
5.Request实现加载资源及显示
上面讲到创建了Request对象,但是Request对象是一个interface,那么Request是在哪里实现的呢?追踪上面创建的Request对象,很容易在RequestBuilder中发现obtainRequest方法,该方法如下:
private Request obtainRequest( Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,RequestOptions requestOptions,
RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,int overrideWidth,int overrideHeight) {
return SingleRequest.obtain(context,glideContext,model,transcodeClass,requestOptions,
overrideWidth,overrideHeight,priority,target,
targetListener, requestListeners, requestCoordinator, glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
该方法通过SingleRequest类的方法obtain去创建一个Request对象,该类实现了Request、ResourceCallback、SizeReadyCallback等接口,可以说这个类就是我们要找的。那么我们看下该类如何实现begin方法的,首先看下源码:
@Override
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting a
// new load etc. This does mean that users who want to restart a load because they expect that
// the view size has changed will need to explicitly clear the View or Target before starting
// the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
方法经过一系列的条件判断,当status是Running或者WAITING_FOR_SIZE状态,并且需要设置占位图的时候,会调用onLoadStarted设置占位图,当status是Status.COMPLETE时会执行ResouceCallback接口的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;
}
//判断是否需要设置资源,如果不需要设置则直接释放资源并设置状态为complete
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
方法主要对条件判断,对于不符合条件的直接跑出异常并调用onLoadFailed回调失败结果,成功了后先判断是否需要设置资源,不设置的话就吧资源释放然后设置状态为complete,否则就调用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;
...
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();
}
可以看到有这么一句:
target.onResourceReady(result, animation);
其实这里就是设置target资源,target分别与要设置资源的组件有关,比如Imageview,对应ImageViewTarget,Glide中对应如下几个Target:
以ImageViewTarget类为例,onReourceReady()方法的实现如下:
@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);
}
protected abstract void setResource(@Nullable Z resource);
那么setResource()抽象方法是在哪里实现的呢,这就要根据设置资源的返回类型决定了,比如我们的返回类型是Drawable,则实现是在DrawableImageViewTarget类中,实现如下:
/**
* A target for display {@link Drawable} objects in {@link ImageView}s.
*/
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
...
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
好了终于看到显示资源的地方了。那么我们回到SingleRequest类的onResourceReady(Resource<R> resource, R result, DataSource dataSource)方法中,最后又一句:
notifyLoadSuccess();
这个方法时做什么的呢,先看notifyLoadSuccess()的源码:
private void notifyLoadSuccess() {
if (requestCoordinator != null) {
requestCoordinator.onRequestSuccess(this);
}
}
方法很简单,就是判断下requestCoordinator 非空,然后执行requestCoordinator对象方法onRequestSuccess(),那么requestCoordinator的实现在哪里呢,搜索很容易发现继承RequestCoordinator接口的只有两个类ThumbnailRequestCoordinator和ErrorRequestCoordinator,打开ThumbnailRequestCoordinator类,找到onRequestSuccess方法,可以看到:
@Override
public void onRequestSuccess(Request request) {
if (request.equals(thumb)) {
return;
}
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 (!thumb.isComplete()) {
thumb.clear();
}
}
从源码中看到这个方法主要做一些加载成功后的清理工作。
由此可以看出,Glide加载成功后,分别由Target和RequestCoordinator接管资源展示和request回收工作。
3、总结
源码分析完了,我们总结出下面一个简易的加载流程图。
总结
- 1.Glide类作为入口类,调用静态方法with(),实例化了Glide这个单例,取得RequestManager对象并返回;
- 2.RequestManager去管理Request请求,根据Activity或Fragment生命周期,管理request请求,包括:onStop,onStart,onDestory,pauseRequest,resumeRequests等,并可以设置加载资源要返回的资源类型,Bitmap,Drawable,Gif。通过调用load()、asBitmap()、asFile()等方法均返回一个RquestBuilder对象,将资源的请求处理移交给了RequestBuilder,实现将功能分离,模块化处理。
- 3.RquestBuilder处理资源加载请求,调用apply()方法传入RequestOptions对象进行一些选项设置;调用transition()方法实现资源加载显示时过度动画;最后调用into()方法,去构建Rquest对象或使用Target设置过的Request对象,调用Rquest的对象方法begin()开启加载请求。
- 4.开启请求后的工作就移交到Request类中,加载完成后,Request调用onResourceReady()方法去判断加载结果,若加载成功,调用Target类的onReadySuccess()方法设置资源,调用RequestCoordinator中方法onRequestSuccess()执行加载成功后的资源清理工作。若加载失败,Request直接调用notifyLoadFailed()方法,将清理资源工作移交给RequestCoordinator类处理。
参考资源:
Glide源码