Android图片加载框架最新解析:从源码的角度理解Glide的执行流程

Natalia ·
更新时间:2024-11-11
· 711 次阅读

文章目录准备源码开始阅读1、with()2、load()3、into()总结
众所周知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)。
在分析的过程中我们不必把源码的每一句都搞懂,那样会把你搞得晕头转向,摸不清思路。因为这些代码都不是一个人写的,同样的功能有多种解决方法。记住我们只需要分析主要的实现逻辑就可以了。
准备好了吗?现在我们开始!

1、with()

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, /*parentHint=*/ 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, /*parentHint=*/ 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), /*targetListener=*/ 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( /*requestLock=*/ new Object(), target, targetListener, /*parentCoordinator=*/ 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



glide 源码 框架 Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号