Android图片加载框架最新解析:从源码的角度理解Glide的执行流程
众所周知Glide是Android开发中普遍使用的图片加载框架,功能非常强大,API非常简便,也是Google官方唯一推荐的图片加载框架。
基本用法,本文不再叙述,详情请参阅官方Github主页
一般情况下,我们想在界面上显示一张图片,只需要一行代码即可实现。图下所示:
Glide.with(this).load(url).into(imageView)
看起来是如此的简洁、完美!但你可能不知道Glide在背后做了多么复杂、繁重的工作,才将图片展示到我们面前。本着知其然也要知其所以然,那么今天我们就来揭开Glide神秘的面纱。
准备源码既然要阅读源码,必须先要把源码下载下来,下载有两种方式:
第一种(推荐):如果项目中已经用到Glide,那么就可以用Android Studio 打开项目,点击Glide的引用直接查看。
第二种:在Glide的Github开源主页下载源码,地址:https://github.com/bumptech/glide
这里推荐第一种,因为使用Android Studio阅读可以进行自由的跳转,方便我们阅读。本文是基于最新的Glide 4.11.0来解析的,随着官方不停的版本迭代,本文也会随着变动不定时更新。读者也可以在下方留言反馈。
问:如何把大象装冰箱里?
答:总共分三步,第一步,Glide.with(this);第二步,load(url);第三步,into(imageView)。
阅读源码的思路是从我们平时使用的语句开始,也就是Glide.with(this)。
在分析的过程中我们不必把源码的每一句都搞懂,那样会把你搞得晕头转向,摸不清思路。因为这些代码都不是一个人写的,同样的功能有多种解决方法。记住我们只需要分析主要的实现逻辑就可以了。
准备好了吗?现在我们开始!
with()方法是Glide类中的一组静态方法,有多个重载方法,如下所示:
public class Glide implements ComponentCallbacks2 {
...
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
...
}
可以看到,with()的重载方法非常多,可以传入Context、Activity、Fragment以及View,其中传入android.app.Fragment的方法已经标记为过时,不建议使用了,我们可以传入androidx.fragment.app.Fragment来替代。 每个with()方法中的逻辑都非常简单,都先是调用getRetriever()静态方法传入Context对象获得一个RequestManagerRetriever对象,然后调用RequestManagerRetriever的get()方法获取RequestManager“请求管理器”对象。
先看下getRetriever()方法:
public class Glide implements ComponentCallbacks2 {
...
@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();
}
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
...
}
通过get()方法中两次判断glide == null,再加上synchronized(),表明这里是一个单例模式,具体的单例细节我们不必深究。这里getRetriever()方法中调用get()方法获取Glide单例对象,然后通过Glide对象获取内部的RequestManagerRetriever对象,RequestManagerRetriever是Glide构造方法中定义的对象。
接下来我们看RequestManagerRetriever的get()方法:
public class RequestManagerRetriever implements Handler.Callback {
...
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(
fragment.getContext(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, null, isActivityVisible(activity));
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
if (Util.isOnBackgroundThread()) {
return get(view.getContext().getApplicationContext());
}
Preconditions.checkNotNull(view);
Preconditions.checkNotNull(
view.getContext(), "Unable to obtain a request manager for a view without a Context");
Activity activity = findActivity(view.getContext());
if (activity == null) {
return get(view.getContext().getApplicationContext());
}
if (activity instanceof FragmentActivity) {
Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
return fragment != null ? get(fragment) : get((FragmentActivity) activity);
}
android.app.Fragment fragment = findFragment(view, activity);
if (fragment == null) {
return get(activity);
}
return get(fragment);
}
...
}
上述代码有很多的get()重载方法,看起来比较复杂,其实梳理一下还是很简单的。不论传入的是Context,还是Fragment等等,实际上只有两种情况而已,即传入Application类型的情况和非Application的情况。
有一点需要说明,通过if (Util.isOnMainThread())判断语句,可以看出,我们如果在非主线程中使用Glide,它都会当成传入Application的情况来处理。
先来看传入Application类型的情况。如果在Glide.with()方法中传入Application对象,这里会调用get(context)中的getApplicationManager(context)方法。如下所示:
public class RequestManagerRetriever implements Handler.Callback {
...
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
...
}
由于Glide本身就是和应用程序生命周期同步的,所以这里并不需要做什么特殊处理。应用关闭,Glide加载也会终止。
上面的情况比较简单,我们再来看传非Application的情况。无论传Fragment还是Activity,最终的流程都是向当前的Activity添加一个隐藏的Fragment。使用这样一个小技巧,这样Glide就可以知道Activity的生命周期了,加载图片时,也会随着Activity的生命周期,判断是否继续加载。代码如下所示:
public class RequestManagerRetriever implements Handler.Callback {
...
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
@NonNull
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
...
}
supportFragmentGet()中调用了getSupportRequestManagerFragment()方法,添加了一个SupportRequestManagerFragment ,SupportRequestManagerFragment 就是一个Fragment。
到这里,with()的源码比较好理解吧,不过越到后面会越复杂,接下来我们开始分析第二步吧!
2、load()我们都知道Glide支持加载图片URL,或是本地路径等多种数据形式的,由于我们不可能把每种都分析一遍,所以这里只选其中之一加载图片URL的方法来研究。使用第一步with()返回的RequestManager对象调用load()方法,可以看出load()方法是在RequestManager类中,如下所示:
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder> {
...
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string);
}
@NonNull
@CheckResult
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public RequestBuilder as(
@NonNull Class resourceClass) {
return new RequestBuilder(glide, this, resourceClass, context);
}
...
}
上述代码很简单,在load()方法中就是先创建一个RequestBuilder对象,再调用RequestBuilder中的load()方法。其实此类中的所有load重载方法逻辑都是调用RequestBuilder对象中load()方法,而且返回类型都是RequestBuilder。那为什么RequestBuilder类中还有很多load()重载方法呢?除了保存数据源,其实只是做了一点初始化配置上的不同而已。下面是RequestBuilder中的load()方法:
public class RequestBuilder extends BaseRequestOptions<RequestBuilder>
implements Cloneable, ModelTypes<RequestBuilder> {
...
@NonNull
@Override
@CheckResult
public RequestBuilder load(@Nullable String string) {
return loadGeneric(string);
}
@NonNull
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
...
}
至此,load()的逻辑就是这些,我们拿到RequestBuilder对象后,就可以调用apply()方法设置我们自定义的配置(RequestOptions对象),例如:placeholder(),error(),priority()等等。接下来我们看第三步into()中的逻辑。
3、into()前面两步真是so easy!现在我们进入Glide加载流程中最复杂的部分看看吧。
代码如下:
public class RequestBuilder extends BaseRequestOptions<RequestBuilder>
implements Cloneable, ModelTypes<RequestBuilder> {
...
@NonNull
public ViewTarget into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
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.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
null,
requestOptions,
Executors.mainThreadExecutor());
}
...
}
前面的一些判断逻辑是关于加载配置的,暂时不用管,我们先来看最后一行,调用glideContext.buildImageViewTarget()方法会构建一个Target对象,Target对象是来展示图片用的,我们跟进去代码如下:
public class GlideContext extends ContextWrapper {
...
@NonNull
public ViewTarget buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
...
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public ViewTarget buildTarget(
@NonNull ImageView view, @NonNull Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
这里又调用了ImageViewTargetFactory类的buildTarget()方法,会根据传入的clazz参数判断是Bitmap就返回BitmapImageViewTarget对象,否则就返回DrawableImageViewTarget对象。至于clazz对象的由来,它是我们在调用asBitmap(),asGif()等方法时 传入的。现在回到into()方法,我们看到又调用了一个into()方法,代码如下:
public class RequestBuilder extends BaseRequestOptions<RequestBuilder>
implements Cloneable, ModelTypes<RequestBuilder> {
...
private <Y extends Target> Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
BaseRequestOptions options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
...
}
这些代码我们只抓核心,第14行buildRequest()构建Request对象,第27行requestManager.track()执行这个Request。
Request是一个非常关键的组件,是用来发出加载图片请求的。我们来看下它是如何构建的:
public class RequestBuilder extends BaseRequestOptions<RequestBuilder>
implements Cloneable, ModelTypes<RequestBuilder> {
...
private Request buildRequest(
Target target,
@Nullable RequestListener targetListener,
BaseRequestOptions requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
new Object(),
target,
targetListener,
null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request buildRequestRecursive(
Object requestLock,
Target target,
@Nullable RequestListener targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions requestOptions,
Executor callbackExecutor) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
Request mainRequest =
buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest =
errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
private Request obtainRequest(
Object requestLock,
Target target,
RequestListener targetListener,
BaseRequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
...
}
这里又调用了buildRequestRecursive()方法,代码虽然长,大部分处理了缩略图的和出错的逻辑,主流程和缩略图逻辑在第42行buildThumbnailRequestRecursive()方法中,由于代码较多,这里梳理下,最终调用了第92行SingleRequest.obtain()返回的是一个SingleRequest对象,把加载图的所有配置参数都封装了进去,我们就不跟进去看了。
ok,现在构建了Request对象,那它是怎么执行的呢?回到刚才的into()方法,我们看下requestManager.track()方法:
public class RequestManager{
...
synchronized void track(@NonNull Target target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
...
}
public class RequestTracker {
...
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);
}
}
...
}
这里主要看track()中的requestTracker.runRequest()方法,这里的逻辑是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
忽略掉暂停请求的功能,我们重点看下begin()方法。由于request实际上是一个SingleRequest对象,所以我们看下SingleRequest类中的begin()方法:
public final class SingleRequest implements Request, SizeReadyCallback, ResourceCallback {
...
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
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 (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
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));
}
}
}
...
}
这里有个细节注意一下,如果model == null,也就是我们传的图片URL地址为null,会走onLoadFailed()方法,设置占位图,具体逻辑感兴趣的可以去看下。我们说下正常情况,这里判断了你有没有使用override() API为图片指定固定宽高。如果指定了,就会执行第29行onSizeReady()方法。没指定,就会执行第31行target.getSize()方法。getSize()方法的内部会根据ImageView的layout_width和layout_height值计算出图片应该的宽高。在计算完之后,它也会调用onSizeReady()方法。那么我们看下onSizeReady()方法:
public final class SingleRequest implements Request, SizeReadyCallback, ResourceCallback {
...
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
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,
callbackExecutor);
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
}
...
}
这里我们主要看onSizeReady()方法中的调用engine.load()方法:
public class Engine{
...
public LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class resourceClass,
Class 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;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
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);
return null;
}
private LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class resourceClass,
Class 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 engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob 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);
}
...
}
在load()方法中逻辑是先调用loadFromMemory()从内存获取数据,没有的话再调用waitForExistingOrStartNewJob()方法,关于缓存的逻辑,我们下一篇再学习,先从104行看起,这里构建了一个EngineJob,它的主要作用就是用来开启线程的,为后面的异步加载图片做准备。接着下面构建了DecodeJob对象,它看起来像是用来对图片进行解码的,但实际上它的任务十分繁重。DecodeJob就是一个Runnable对象,下面就调用了engineJob.start()方法,在子线程当中执行DecodeJob。我们来看看DecodeJob中的run()方法执行了什么?
class DecodeJob
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob>,
Poolable {
...
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
t);
}
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
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);
}
}
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);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
...
}
这里主要看run()中的runWrapped()方法,runWrapped()方法的逻辑是判断运行原因,在首次运行时runReason是初始值INITIALIZE,接下来调用getNextStage()获取下一步的状态,getNextStage()方法中会判断是否有缓存数据,具体的缓存逻辑暂不研究,我们先假设没有缓存,这时迭代此方法走RESOURCE_CACHE分支,我们再假设没有缓存,会迭代方法走DATA_CACHE分支,这里onlyRetrieveFromCache表示“仅从缓存里检索数据”,现在Glide还没找到数据,如果为true,流程就会结束,所以我们假设为false,返回Stage.SOURCE状态。然后getNextGenerator()方法,就会走SOURCE分支并返回SourceGenerator对象。我们要记住这时的currentGenerator值是一个SourceGenerator对象。因为下面我们要用到它,我们来看下runGenerators()方法:
class DecodeJob{
...
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();
}
}
...
}
这里比较绕,while循环里面的getNextStage(),getNextGenerator()我们已经分析过了,这里重点是看下第9行currentGenerator.startNext()方法。我们知道currentGenerator是一个SourceGenerator对象,所以startNext()方法在SourceGenerator类中,代码如下:
class SourceGenerator {
...
@Override
public boolean startNext() {
if (dataToCache != null) {
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;
startNextLoad(loadData);
}
}
return started;
}
private void startNextLoad(final LoadData toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback() {
@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);
}
}
});
}
...
}
可以看到startNext()中又调用了startNextLoad()方法,我重点看第19行和第31行,先说第19行,作用是获取数据加载器,代码如下:
final class DecodeHelper {
...
List<LoadData> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader modelLoader = modelLoaders.get(i);
LoadData current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
...
}
第7行这里从Glide实例的Registry注册表中获取modelLoaders。
注意:在注册表中注册的都是ModelLoader的实现ModelLoaderFactory静态工厂类,当调用Registry的getModelLoaders时会调用工厂类中的build()方法。
因为我们以String为例,所以这里的model是String类型。我们来看String类型有哪些modelLoaders:
public class Glide{
...
Glide(){
registry
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
}
...
}
这里我们以StringLoader.StreamFactory为例子,调用了getModelLoaders()方法,会执行StringLoader.StreamFactory的build()方法。代码如下:
public class StringLoader{
public static class StreamFactory{
@NonNull
@Override
public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader(multiFactory.build(Uri.class, InputStream.class));
}
}
}
在build()方法中构建了StringLoader对象,里面调用了multiFactory.build()方法,这时我们需要在Glide的registry注册表中找到参数为Uri.class, InputStream.class的Factory对象。这时候的重点是看HttpUriLoader.Factory().build()。
registry
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
就这样不断追踪下去最终返回的是HttpGlideUrlLoader对象:
public class HttpGlideUrlLoader{
...
public static class Factory{
@NonNull
@Override
public ModelLoader build(MultiModelLoaderFactory multiFactory) {
return new HttpGlideUrlLoader(modelCache);
}
}
...
}
接下来我们看看StringLoader构造方法:
public class StringLoader{
...
public StringLoader(ModelLoader uriLoader) {
this.uriLoader = uriLoader;
}
...
}
就是把HttpGlideUrlLoader对象封装进去,现在我们回到刚才的DecodeHelper.getLoadData()方法,获取完modelLoaders,遍历了一遍并执行modelLoader.buildLoadData()方法构建loadData对象。我们以modelLoader为上面分析的StringLoader对象继续看buildLoadData()的实现:
public class StringLoader{
...
@Override
public LoadData buildLoadData(
@NonNull String model, int width, int height, @NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
...
}
这里调用了uriLoader.buildLoadData(),也就是HttpGlideUrlLoader类中的buildLoadData()方法:
public class HttpGlideUrlLoader{
...
@Override
public LoadData buildLoadData(
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
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);
return new LoadData(url, new HttpUrlFetcher(url, timeout));
}
...
}
可以看到,最后返回的是封装了HttpUrlFetcher的LoadData对象。现在我们回到SourceGenerator的startNextLoad()方法:
class SourceGenerator{
...
private void startNextLoad(final LoadData toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback() {
@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);
}
}
});
}
...
}
我可以得知loadData的fetcher既是HttpUrlFetcher对象,所以这里调用的loadData()方法是在HttpUrlFetcher类中:
public class HttpUrlFetcher{
...
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback 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));
}
}
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
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 headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
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);
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);
}
}
...
}
可以看到,这里的loadData()调用loadDataWithRedirects()方法获取数据流,loadDataWithRedirects方法使用了HttpURLConnection从网络获取数据。我们费了九牛二虎之力终于找到网络请求代码。
现在有了InputStream,还需要把它回调出去,在第9行调用了callback.onDataReady()方法,从上一步的startNextLoad()方法得知调用了SourceGenerator类的onDataReadyInternal()方法,代码如下:
class SourceGenerator{
...
void onDataReadyInternal(LoadData loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = this.helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
this.dataToCache = data;
this.cb.reschedule();
} else {
this.cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), this.originalKey);
}
}
...
}
这里有个是否要缓存的策略,我们先不缓存直接看onDataFetcherReady()方法,这里的cb对象是在SourceGenerator对象创建时传入的,还记得SourceGenerator对象在什么时候创建的吗?是在DecodeJob运行时调用了getNextGenerator()方法创建的。DecodeJob本身实现了onDataFetcherReady()方法:
class DecodeJob
implements DataFetcherGenerator.FetcherReadyCallback{
...
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey(
"Retrieved data",
startFetchTime,
"data: "
+ currentData
+ ", cache key: "
+ currentSourceKey
+ ", fetcher: "
+ currentFetcher);
}
Resource 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();
}
}
...
}
第18行onDataFetcherReady()中调用了decodeFromRetrievedData()方法解码数据,第39行decodeFromRetrievedData()通过调用decodeFromData()方法解码数据返回Resource对象,然后第45行调用notifyEncodeAndRelease()通知获取数据成功。代码如下:
class DecodeJob{
...
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource result = resource;
LockedResource 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();
}
private void notifyComplete(Resource resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
...
}
这里notifyEncodeAndRelease()调用了notifyComplete()方法通知完成,notifyComplete()调用callback.onResourceReady()进行回调数据,onResourceReady()在EngineJob类中实现了此方法:
class EngineJob{
...
@Override
public void onResourceReady(Resource resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
@Synthetic
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
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 = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
engineJobListener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
...
}
onResourceReady()调用notifyCallbacksOfResult()通知回调结果,notifyCallbacksOfResult()中主要看第40行,执行了一个线程CallResourceReady(),这个线程做了什么呢,我们看它的run()方法:
class EngineJob{
...
@Override
private class CallResourceReady implements Runnable {
public void run() {
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
}
@Synthetic
@GuardedBy("this")
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
...
}
run()中调用了callCallbackOnResourceReady(),然后调用cb.onResourceReady回调数据,cb就是SingleRequest对象,由于代码较多这里直接给出关键代码:
public final class SingleRequest{
...
private void onResourceReady(){
...
target.onResourceReady(result, animation);
...
}
...
}
这里的target就是调用into()方法时构建的DrawableImageViewTarget对象。所以这里就进入了DrawableImageViewTarget父类ImageViewTarget的onResourceReady()方法,再调用DrawableImageViewTarget的setResource()方法,过程方法比较简单就不列了,这里直接给出最终的方法:
public class DrawableImageViewTarget extends ImageViewTarget {
...
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
}
到此就把获取到的图片数据通过setImageDrawable()方法设置给了imageView控件,把图片展示出来了。
总结看到这里,是不是感觉晕晕的?Glide帮我们做了非常多的事情,回调数据当然也很深。
真难相信这么短短的一行代码:Glide.with(this).load(url).into(imageView); 竟然如此的复杂,我们还仅仅是对流程简单的分析,我们下一篇再来探索下Glide的缓存机制。
参考资料
郭神 Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
作者:小石头93
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341