Glide源码解析
An image loading and caching library for Android focused on smooth scrolling
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道( decode pipeline
),以及自动的资源池技术。
Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于 HttpUrlConnection
的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
使用
// For a simple view: @Override public void onCreate(Bundle savedInstanceState) { ... ImageView imageView = (ImageView) findViewById(R.id.my_image_view); Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView); }
更多
源码
Glide#with(…)
with()
在 Glide 类中好几个方法重载,可以接收 Context
、 Activity
、 Fragment
等类型参数
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); } @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); } @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(); } // ... }
这些方法都是通过 getRetriever(@Nullable Context context)
方法来获取 RequestManagerRetriever
对象,而其中 Glide.get(context)
进行了初始化等操作
Glide#get(Context context)
public class Glide implements ComponentCallbacks2 { // ... /** * Get the singleton. * * @return the singleton */ @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; } private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) { GeneratedAppGlideModule result = null; try { Class clazz = (Class) Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl"); result = clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext()); } catch (ClassNotFoundException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w( TAG, "Failed to find GeneratedAppGlideModule. You should include an" + " annotationProcessor compile dependency on com.github.bumptech.glide:compiler" + " in your application and a @GlideModule annotated AppGlideModule implementation" + " or LibraryGlideModules will be silently ignored"); } // These exceptions can't be squashed across all versions of Android. } catch (InstantiationException e) { throwIncorrectGlideModule(e); } catch (IllegalAccessException e) { throwIncorrectGlideModule(e); } catch (NoSuchMethodException e) { throwIncorrectGlideModule(e); } catch (InvocationTargetException e) { throwIncorrectGlideModule(e); } return result; } @GuardedBy("Glide.class") private static void checkAndInitializeGlid e( @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) { // In the thread running initGlide(), one or more classes may call Glide.get(context). // Without this check, those calls could trigger infinite recursion. if (isInitializing) { throw new IllegalStateException( "You cannot call Glide.get() in registerComponents()," + " use the provided Glide instance instead"); } isInitializing = true; initializeGlide(context, generatedAppGlideModule); isInitializing = false; } // ... }
com.bumptech.glide.GeneratedAppGlideModuleImpl
会通过 apt 生成,路径为 build/generated/source/apt/debug/com/bumptech/glide/GeneratedAppGlideModuleImpl.java
,该对象中会持有我们进行配置的类( extends AppGlideModule
),若没有在 java 代码中配置或者 isManifestParsingEnabled()
返回的 true
,则会继续从 AndroidManifest 中去读取配置,主要操作都在 initializeGlide()
中进行
RequestManagerRetriever#get(…)
之前说到的可以接收 Context
、 Activity
、 Fragment
等类型参数,具体就是在这个进行判断操作
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 // Only unwrap a ContextWrapper if the baseContext has a non-null application context. // Context#createPackageContext may return a Context without an Application instance, // in which case a ContextWrapper may be used to attach one. && ((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()); // The view might be somewhere else, like a service. if (activity == null) { return get(view.getContext().getApplicationContext()); } // Support Fragments. // Although the user might have non-support Fragments attached to FragmentActivity, searching // for non-support Fragments is so expensive pre O and that should be rare enough that we // prefer to just fall back to the Activity directly. if (activity instanceof FragmentActivity) { Fragment fragment = findSupportFragment(view, (FragmentActivity) activity); return fragment != null ? get(fragment) : get(activity); } // Standard Fragments. android.app.Fragment fragment = findFragment(view, activity); if (fragment == null) { return get(activity); } return get(fragment); } // ... }
get(Context context)
:
-
非主线程的话,当
Application
处理,Glide 加载图片的生命周期是和 with 里的参数保持一致,而对于Application
类型,只有当应用程序被杀掉的时候,图片加载才会停止 -
主线程情况下,对
Context
进行类型判断,其他类型确定的会对应到重载的方法中
对于其他重载方法:
-
进行了线程的判断,若非主线程的话,那么走
get(Context context)
方法 -
若是主线程
-
Activity
: 拿到FragmentManager
对象,调用fragmentGet()
方法 -
android.app.Fragment
: 获取对应的 Activity,再走 1 流程 -
androidx.fragment.app.Fragment
: 拿到FragmentManager
对象,调用supportFragmentGet()
方法 -
FragmentActivity
: 拿到FragmentManager
对象,调用supportFragmentGet()
方法 -
View
: 通过View#getContext
找到Activity
-
Activity
为 null,走Application
流程 -
是否是
FragmentActivity
,是的话走FragmentActivity
流程 -
通过
Activity
的getFragmentManager()
判断是否有android.app.Fragment
,有的话走对应流程 -
最后走
Activity
流程
-
-
RequestManagerRetriever#fragmentGet(…)
public class RequestManagerRetriever implements Handler.Callback { //... private RequestManager fragmentGet( @NonNull Context context, @NonNull android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) { RequestManagerFragment current = getRequestManagerFragment(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; } private RequestManagerFragment getRequestManagerFragment( @NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); current.setParentFragmentHint(parentHint); if (isParentVisible) { current.getGlideLifecycle().onStart(); } pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; } // ... }
-
通过
fm.findFragmentByTag(FRAGMENT_TAG)
找到RequestManagerFragment
pendingRequestManagerFragments
-
通过
ActivityFragmentLifecycle
和isParentVisible
来处理生命周期 -
添加到
Activity
的FragmentManager
中 -
发送一个
ID_REMOVE_FRAGMENT_MANAGER
消息,处理逻辑就是删除pendingRequestManagerFragments
中 key 为FragmentManager
的对象 -
从
RequestManagerFragment
中获取RequestManager
,若没有的话通过RequestManagerFactory#build()
的方式创建一个,最后返回
RequestManagerFragment
的作用就是为了方便 Glide 控制生命周期,因为 Fragment 的生命周期和 Activity 是同步的,如果 Activity 被销毁了,那么 Fragment 是可以监听到的,这样 Glide 就可以捕获这个事件并停止图片加载
RequestManager#load(…)
public class RequestManager implements LifecycleListener, ModelTypes<RequestBuilder> { // ... public RequestBuilder load(@Nullable Bitmap bitmap) { return asDrawable().load(bitmap); } public RequestBuilder load(@Nullable Drawable drawable) { return asDrawable().load(drawable); } public RequestBuilder load(@Nullable String string) { return asDrawable().load(string); } public RequestBuilder load(@Nullable Uri uri) { return asDrawable().load(uri); } public RequestBuilder load(@Nullable File file) { return asDrawable().load(file); } public RequestBuilder load(@RawRes @DrawableRes @Nullable Integer resourceId) { return asDrawable().load(resourceId); } public RequestBuilder load(@Nullable URL url) { return asDrawable().load(url); } public RequestBuilder load(@Nullable byte[] model) { return asDrawable().load(model); } public RequestBuilder load(@Nullable Object model) { return asDrawable().load(model); } public RequestBuilder asDrawable() { return as(Drawable.class); } public RequestBuilder as( @NonNull Class resourceClass) { return new RequestBuilder(glide, this, resourceClass, context); } // ... }
若没有指定类型,则调用的是 load(Object model)
,这里都是通过 asDrawable()
来获取 RequestBuilder
对象,同样除了 asDrawable()
还有其他方法 asBitmap()
、 asGif()
、 asFile()
,最终都是调用的 as(Class resourceClass)
方法,该方法通过 Class
的形式忽略掉了具体类型
RequestBuilder#load(…)
public class RequestBuilder extends BaseRequestOptions<RequestBuilder> implements Cloneable, ModelTypes<RequestBuilder> { // ... public RequestBuilder load(@Nullable Drawable drawable) { return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE)); } private RequestBuilder loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; } public RequestBuilder apply(@NonNull BaseRequestOptions requestOptions) { Preconditions.checkNotNull(requestOptions); return super.apply(requestOptions); } // ... }
RequestBuilder
的 load()
也有很多重载方法,这里就按 Drawable 类型的来讲; apply()
方法的作用是将 RequestOptions
配置应用到 ReqiestBuilder
RequestManager#into(ImageView view)
public class RequestBuilder extends BaseRequestOptions<RequestBuilder> implements Cloneable, ModelTypes<RequestBuilder> { // ... public ViewTarget into(@NonNull ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view); BaseRequestOptions requestOptions = this; if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { // Clone in this method so that if we use this RequestBuilder to load into a View and then // into a different target, we don't retain the transformation applied based on the previous // View's scale type. switch (view.getScaleType()) { case CENTER_CROP: requestOptions = requestOptions.clone().optionalCenterCrop(); break; case CENTER_INSIDE: requestOptions = requestOptions.clone().optionalCenterInside(); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions = requestOptions.clone().optionalFitCenter(); break; case FIT_XY: requestOptions = requestOptions.clone().optionalCenterInside(); break; case CENTER: case MATRIX: default: // Do nothing. } } return into( glideContext.buildImageViewTarget(view, transcodeClass), /*targetListener=*/ null, requestOptions, Executors.mainThreadExecutor()); } // ... }
该方法返回一个 ViewTarget
对象,会把 ImageView
封装进去,也是在该类里面进行 Drawable 的展示
GlideContext#buildImageViewTarget()
public class GlideContext extends ContextWrapper { // ... @NonNull public ViewTarget buildImageViewTarget( @NonNull ImageView imageView, @NonNull Class transcodeClass) { return imageViewTargetFactory.buildTarget(imageView, transcodeClass); } // ... }
ViewTarget
的创建是通过 ImageViewTargetFactory
工厂来实现的
ImageViewTargetFactory#buildTarget(ImageView view, Class clazz)
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)"); } } }
再来看下 DrawableImageViewTarget
DrawableImageViewTarget
public class DrawableImageViewTarget extends ImageViewTarget { public DrawableImageViewTarget(ImageView view) { super(view); } /** @deprecated Use {@link #waitForLayout()} instead. */ // Public API. @SuppressWarnings({"unused", "deprecation"}) @Deprecated public DrawableImageViewTarget(ImageView view, boolean waitForLayout) { super(view, waitForLayout); } @Override protected void setResource(@Nullable Drawable resource) { view.setImageDrawable(resource); } }
DrawableImageViewTarget
继承于 ImageViewTarget
类,父类实现了 LifecycleListener
接口,在不同的生命周期中做不动的事情,比如在 onLoadStarted()
时给 ImageView 塞 placeHolder 的 Drawable,在 onLoadFailed
时给 ImageView 塞 error 的 Drawable
RequestManager#into(Y target, RequestListener targetListener, BaseRequestOptions options, Executor callbackExecutor)
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)) { request.recycle(); // If the request is completed, beginning again will ensure the result is re-delivered, // triggering RequestListeners and Targets. If the request is failed, beginning again will // restart the request, giving it another chance to complete. If the request is already // running, we can let it continue running without interruption. if (!Preconditions.checkNotNull(previous).isRunning()) { // Use the previous request rather than the new one to allow for optimizations like skipping // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions // that are done in the individual Request. previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); requestManager.track(target, request); return target; } // ... }
-
通过
buildRequest(target, targetListener, options, callbackExecutor)
获取请求的Request
-
target.getRequest()
判断这个ViewTarger
是否已经有了Request
-
若为等效
Request
、MemoryCache 不为skipMemoryCache
、已经完成,将当前新生成的 Request 进行回收、释放资源,老Request
调用begin()
方法 -
没有
Request
或者不满足条件的话,将其设置给ViewTarget
,通过RequestManager#track
进行下一步操作
RequestManager#track(Target target, Request request)
public class RequestManager implements LifecycleListener, ModelTypes<RequestBuilder> { // ... synchronized void track(@NonNull Target target, @NonNull Request request) { targetTracker.track(target); requestTracker.runRequest(request); } // ... }
runRequest
是请求的开始
RequestTracker#(Request 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); } } // ... }
又走到了 request.begin()
SingleRequest#begin()
public final class SingleRequest implements Request, SizeReadyCallback, ResourceCallback, FactoryPools.Poolable { // ... @Override public synchronized 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)); } } public synchronized void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); if (IS_VERBOSE_LOGGABLE) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { 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); // This is a hack that's only useful for testing right now where loads complete synchronously // even though under any executor running on any thread but the main thread, the load would // have completed asynchronously. if (status != Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } // ... }
-
判断宽高是否有效,若无效则调用
onLoadFailed()
,然后 return -
当前状态为
Status.RUNNING
的话,抛异常 -
当前状态为
Status.COMPLETE
的话,调用onResourceReady()
,将 resource 通过RequestListener
的onResourceReady()
返回出去,然后 return -
将当前状态设置成
Status.WAITING_FOR_SIZE
-
若当前宽高又明确的值的话,调用
onSizeReady()
进行下一步操作,同时将状态变为Status.RUNNING
;没有的话调用target.getSize(SizeReadyCallback)
来获取,获取到之后通过SizeReadyCallback
回调返回 (若 View 宽高还未确定的情况下会通过 ViewTreeObserver 的 OnPreDrawListener 来监听) -
当状态为
Status.RUNNING
或者status == Status.WAITING_FOR_SIZE
且有 placeholder 的情况下,调用target.onLoadStarted(getPlaceholderDrawable())
Engine#load(…)
public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, EngineResource.ResourceListener { // ... 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; } @Nullable private EngineResource loadFromMemory( EngineKey key, boolean isMemoryCacheable, long startTime) { if (!isMemoryCacheable) { return null; } EngineResource active = loadFromActiveResources(key); if (active != null) { if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return active; } EngineResource cached = loadFromCache(key); if (cached != null) { if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return cached; } return null; } @Nullable private EngineResource loadFromActiveResources(Key key) { EngineResource active = activeResources.get(key); if (active != null) { active.acquire(); } return active; } private EngineResource loadFromCache(Key key) { EngineResource cached = getEngineResourceFromCache(key); if (cached != null) { cached.acquire(); activeResources.activate(key, cached); } return cached; } private EngineResource getEngineResourceFromCache(Key key) { Resource cached = cache.remove(key); final EngineResource result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource) cached; } else { result = new EngineResource( cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this); } return result; } // ... }
-
构建
EngineKey
-
调用
loadFromMemory()
尝试从缓存中获取-
通过
loadFromActiveResources()
从ActiveResources
中获取,这里在内存存了一个 Map -
若找不到,接着通过
loadFromCache()
从MemoryCache
获取,该对象可配置,如获取到则在activeResources
中放一份
-
通过
-
没有缓存的话,通过
waitForExistingOrStartNewJob()
去请求
Engine#waitForExistingOrStartNewJob(…)
public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, EngineResource.ResourceListener { // ... 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); } // ... }
-
通过
jobs.get()
来判断是否已经在队列里面了,若在的话再添加个 callback 然后 return -
通过
EngineJobFactory
构建一个EngineJob
,通过DecodeJobFactory
构建一个DecodeJob
,放入到Jobs
中 -
通过
EngineJob#start()
开始请求
EngineJob#start(DecodeJob decodeJob)
class EngineJob implements DecodeJob.Callback, Poolable { // ... public synchronized void start(DecodeJob decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); } // ... }
判断是否从缓存中获取,然后拿到对应的线程池执行 DecodeJob
DecodeJob#run()
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback, Runnable, Comparable<DecodeJob>, Poolable { // ... @Override public void run() { // This should be much more fine grained, but since Java's thread pool implementation silently // swallows all otherwise fatal exceptions, this will at least make it obvious to developers // that something is failing. GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model); // Methods in the try statement can invalidate currentFetcher, so set a local variable here to // ensure that the fetcher is cleaned up either way. DataFetcher localFetcher = currentFetcher; try { if (isCancelled) { notifyFailed(); return; } runWrapped(); } catch (CallbackException e) { // If a callback not controlled by Glide throws an exception, we should avoid the Glide // specific debug logic below. throw e; } catch (Throwable t) { // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We // are however ensuring that our callbacks are always notified when a load fails. Without this // notification, uncaught throwables never notify the corresponding callbacks, which can cause // loads to silently hang forever, a case that's especially bad for users using Futures on // background threads. if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t); } // When we're encoding we've already notified our callback and it isn't safe to do so again. if (stage != Stage.ENCODE) { throwables.add(t); notifyFailed(); } if (!isCancelled) { throw t; } throw t; } finally { // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call // close in all cases anyway. 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: // 产生含有降低采样/转换资源数据缓存文件的 DataFetcher return new ResourceCacheGenerator(decodeHelper, this); case DATA_CACHE: // 产生包含原始未修改的源数据缓存文件的 DataFetcher return new DataCacheGenerator(decodeHelper, this); case SOURCE: // 生成使用注册的 ModelLoader 和加载时提供的 Model 获取源数据规定的 DataFetcher // 根据不同的磁盘缓存策略,源数据可首先被写入到磁盘,然后从缓存文件中加载,而不是直接返回 return new SourceGenerator(decodeHelper, this); case FINISHED: return null; default: throw new IllegalStateException("Unrecognized stage: " + stage); } } private void runGenerators() { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } // We've run out of stages and generators, give up. if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } // Otherwise a generator started a new load and we expect to be called back in // onDataFetcherReady. } // ... }
最开始的状态为 INITIALIZE
-
getNextStage()
返回的Stage
的作用是标识当前为哪个阶段,不同阶段做不同的事;在还没有缓存情况,这里首先应该做的是加载;所以递归调用这个函数,最终返回的Stage.SOURCE
-
getNextGenerator()
通过Stage
找到数据获取生成器 -
runGenerators()
去数据获取
SourceGenerator#startNext()
class SourceGenerator implements DataFetcherGenerator, DataFetcher.DataCallback, DataFetcherGenerator.FetcherReadyCallback { // ... @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; loadData.fetcher.loadData(helper.getPriority(), this); } } return started; } // ... }
然后根据 ModelLoader.LoadData 的 fetcher 加载数据 loadData.fetcher.loadData(helper.getPriority(), this);
HttpUrlFetcher#loadData(Priority priority, DataCallback callback)
public class HttpUrlFetcher implements DataFetcher { // ... @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 { // Comparing the URLs using .equals performs additional network I/O and is generally broken. // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html. try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new HttpException("In re-direct loop"); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } 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); // Stop the urlConnection instance of HttpUrlConnection from following redirects so that // redirects will be handled by recursive calls to this method, loadDataWithRedirects. urlConnection.setInstanceFollowRedirects(false); // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352. stream = urlConnection.getInputStream(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); } else if (isHttpRedirect(statusCode)) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); // Closing the stream specifically is required to avoid leaking ResponseBodys in addition // to disconnecting the url connection below. See #2352. cleanup(); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else if (statusCode == INVALID_STATUS_CODE) { throw new HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } } // ... }
这里就是网络请求,请求成功后通过 callback.onDataReady(result)
返回出去
SourceGenerator#onDataReady(Object data)
class SourceGenerator implements DataFetcherGenerator, DataFetcher.DataCallback, DataFetcherGenerator.FetcherReadyCallback { // ... @Override public void onDataReady(Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; // We might be being called back on someone else's thread. Before doing anything, we should // reschedule to get back onto Glide's thread. cb.reschedule(); } else { cb.onDataFetcherReady( loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } } // ... }
-
若有缓存,则通过
cb.reschedule()
重新走到DecodeJob#run()
方法中(runReason 变为SWITCH_TO_SOURCE_SERVICE
) -
没有缓存,则通过
onDataFetcherReady
走到DecodeJob#onDataFetcherReady()
中
DecodeJob#onDataFetcherReady(Key sourceKey, Object data, DataFetcher fetcher, DataSource dataSource, Key attemptedKey)
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback, Runnable, Comparable<DecodeJob>, Poolable { // ... @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 Resource decodeFromData( DataFetcher fetcher, Data data, DataSource dataSource) throws GlideException { try { if (data == null) { return null; } long startTime = LogTime.getLogTime(); Resource result = decodeFromFetcher(data, dataSource); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Decoded result " + result, startTime); } return result; } finally { fetcher.cleanup(); } } private Resource decodeFromFetcher(Data data, DataSource dataSource) throws GlideException { LoadPath path = decodeHelper.getLoadPath((Class) data.getClass()); return runLoadPath(data, dataSource, path); } private Resource runLoadPath( Data data, DataSource dataSource, LoadPath path) throws GlideException { Options options = getOptionsWithHardwareConfig(dataSource); DataRewinder rewinder = glideContext.getRegistry().getRewinder(data); try { // ResourceType in DecodeCallback below is required for compilation to work with gradle. return path.load( rewinder, options, width, height, new DecodeCallback(dataSource)); } finally { rewinder.cleanup(); } } // ... }
-
将 runReason 改为
RunReason.DECODE_DATA
,若线程不对的话,切线程,重新走到DecodeJob#run()
方法中 -
通过
decodeFromData()
解码
LoadPath#load(…)
public class LoadPath { // ... public Resource load( DataRewinder rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback decodeCallback) throws GlideException { List throwables = Preconditions.checkNotNull(listPool.acquire()); try { return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables); } finally { listPool.release(throwables); } } private Resource loadWithExceptionList( DataRewinder rewinder, @NonNull Options options, int width, int height, DecodePath.DecodeCallback decodeCallback, List exceptions) throws GlideException { Resource result = null; //noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decodePaths.size(); i < size; i++) { DecodePath path = decodePaths.get(i); try { result = path.decode(rewinder, width, height, options, decodeCallback); } catch (GlideException e) { exceptions.add(e); } if (result != null) { break; } } if (result == null) { throw new GlideException(failureMessage, new ArrayList(exceptions)); } return result; } public Resource decode( DataRewinder rewinder, int width, int height, @NonNull Options options, DecodeCallback callback) throws GlideException { Resource decoded = decodeResource(rewinder, width, height, options); Resource transformed = callback.onResourceDecoded(decoded); return transcoder.transcode(transformed, options); } @NonNull private Resource decodeResource( DataRewinder rewinder, int width, int height, @NonNull Options options) throws GlideException { List exceptions = Preconditions.checkNotNull(listPool.acquire()); try { return decodeResourceWithList(rewinder, width, height, options, exceptions); } finally { listPool.release(exceptions); } } @NonNull private Resource decodeResourceWithList( DataRewinder rewinder, int width, int height, @NonNull Options options, List exceptions) throws GlideException { Resource result = null; //noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decoders.size(); i < size; i++) { ResourceDecoder decoder = decoders.get(i); try { DataType data = rewinder.rewindAndGet(); if (decoder.handles(data, options)) { data = rewinder.rewindAndGet(); result = decoder.decode(data, width, height, options); } // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but // instead log and continue. See #2406 for an example. } catch (IOException | RuntimeException | OutOfMemoryError e) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Failed to decode data for " + decoder, e); } exceptions.add(e); } if (result != null) { break; } } if (result == null) { throw new GlideException(failureMessage, new ArrayList(exceptions)); } return result; } // ... }
最终调用到了 decodeResourceWithList()
方法,通过 decoder.decode(data, width, height, options)
进行解码
BitmapDrawableDecoder#decode(DataType source, int width, int height, Options options)
public class BitmapDrawableDecoder implements ResourceDecoder { // ... @Override public Resource decode( @NonNull DataType source, int width, int height, @NonNull Options options) throws IOException { Resource bitmapResource = decoder.decode(source, width, height, options); return LazyBitmapDrawableResource.obtain(resources, bitmapResource); } // ... }
这里的 decoder
是 StreamBitmapDecoder
StreamBitmapDecoder#decode(DataType source, int width, int height, Options options)
public class StreamBitmapDecoder implements ResourceDecoder { // ... @Override public Resource decode( @NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException { // Use to fix the mark limit to avoid allocating buffers that fit entire images. final RecyclableBufferedInputStream bufferedStream; final boolean ownsBufferedStream; if (source instanceof RecyclableBufferedInputStream) { bufferedStream = (RecyclableBufferedInputStream) source; ownsBufferedStream = false; } else { bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool); ownsBufferedStream = true; } // Use to retrieve exceptions thrown while reading. // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a // way to determine if a Bitmap is partially decoded, consider removing. ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream); // Use to read data. // Ensures that we can always reset after reading an image header so that we can still // attempt to decode the full image even when the header decode fails and/or overflows our read // buffer. See #283. MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream); UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream); try { return downsampler.decode(invalidatingStream, width, height, options, callbacks); } finally { exceptionStream.release(); if (ownsBufferedStream) { bufferedStream.release(); } } } // ... }
最后调用到的是 downsampler
的 decode()
Downsampler#decode(InputStream is, int requestedWidth, int requestedHeight, Options options, DecodeCallbacks callbacks)
public final class Downsampler { // ... public Resource decode( InputStream is, int requestedWidth, int requestedHeight, Options options, DecodeCallbacks callbacks) throws IOException { Preconditions.checkArgument( is.markSupported(), "You must provide an InputStream that supports" + " mark()"); byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class); BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions(); bitmapFactoryOptions.inTempStorage = bytesForOptions; DecodeFormat decodeFormat = options.get(DECODE_FORMAT); PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE); DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION); boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS); boolean isHardwareConfigAllowed = options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG); try { Bitmap result = decodeFromWrappedStreams( is, bitmapFactoryOptions, downsampleStrategy, decodeFormat, preferredColorSpace, isHardwareConfigAllowed, requestedWidth, requestedHeight, fixBitmapToRequestedDimensions, callbacks); return BitmapResource.obtain(result, bitmapPool); } finally { releaseOptions(bitmapFactoryOptions); byteArrayPool.put(bytesForOptions); } } // ... }
通过 decodeFromWrappedStreams()
解出 Bitmap,然后封装成 BitmapResource
DecodeJob#decodeFromRetrievedData()
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback, Runnable, Comparable<DecodeJob>, Poolable { // ... 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(); } } 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); } interface Callback { void onResourceReady(Resource resource, DataSource dataSource); void onLoadFailed(GlideException e); void reschedule(DecodeJob job); } // ... }
重新回到 decodeFromRetrievedData()
方法中,通过 decodeFromData()
获取到 resource 之后通过 notifyEncodeAndRelease()
回调出去
EngineJob#onResourceReady(Resource resource, DataSource dataSource)
class EngineJob implements DecodeJob.Callback, Poolable { // ... @Override public void onResourceReady(Resource resource, DataSource dataSource) { synchronized (this) { this.resource = resource; this.dataSource = dataSource; } notifyCallbacksOfResult(); } void notifyCallbacksOfResult() { ResourceCallbacksAndExecutors copy; Key localKey; EngineResource localResource; synchronized (this) { stateVerifier.throwIfRecycled(); if (isCancelled) { // TODO: Seems like we might as well put this in the memory cache instead of just recycling // it since we've gotten this far... 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); // Hold on to resource for duration of our callbacks below so we don't recycle it in the // middle of notifying if it synchronously released by one of the callbacks. Acquire it under // a lock here so that any newly added callback that executes before the next locked section // below can't recycle the resource before we call the callbacks. 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(); } private class CallResourceReady implements Runnable { private final ResourceCallback cb; CallResourceReady(ResourceCallback cb) { this.cb = cb; } @Override public void run() { // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock // (b/136032534). synchronized (cb) { synchronized (EngineJob.this) { if (cbs.contains(cb)) { // Acquire for this particular callback. engineResource.acquire(); callCallbackOnResourceReady(cb); removeCallback(cb); } decrementPendingCallbacks(); } } } } void callCallbackOnResourceReady(ResourceCallback cb) { try { // This is overly broad, some Glide code is actually called here, but it's much // simpler to encapsulate here than to do so at the actual call point in the // Request implementation. cb.onResourceReady(engineResource, dataSource); } catch (Throwable t) { throw new CallbackException(t); } } // ... }
最后通过 cb.onResourceReady(engineResource, dataSource)
回调出去
SingleRequest#onResourceReady(Resource resource, DataSource dataSource)
public final class SingleRequest implements Request, SizeReadyCallback, ResourceCallback, FactoryPools.Poolable { // ... public synchronized void onResourceReady(Resource resource, DataSource dataSource) { Resource toRelease = null; try { synchronized (this) { stateVerifier.throwIfRecycled(); loadStatus = null; if (resource == null) { GlideException exception = new GlideException( "Expected to receive a Resource with an " + "object of " + transcodeClass + " inside, but instead got null."); onLoadFailed(exception); return; } Object received = resource.get(); if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) { toRelease = resource; this.resource = null; GlideException exception = new GlideException( "Expected to receive an object of " + transcodeClass + " but instead" + " got " + (received != null ? received.getClass() : "") + "{" + received + "} inside" + " " + "Resource{" + resource + "}." + (received != null ? "" : " " + "To indicate failure return a null Resource " + "object, rather than a Resource object containing null data.")); onLoadFailed(exception); return; } if (!canSetResource()) { toRelease = resource; this.resource = null; // We can't put the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } onResourceReady((Resource) resource, (R) received, dataSource); } } finally { if (toRelease != null) { engine.release(toRelease); } } } private synchronized void onResourceReady(Resource resource, R result, DataSource dataSource) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; if (glideContext.getLogLevel() <= Log.DEBUG) { Log.d( GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from " + dataSource + " for " + model + " with size [" + width + "x" + height + "] in " + LogTime.getElapsedMillis(startTime) + " ms"); } isCallingCallbacks = true; try { boolean anyListenerHandledUpdatingTarget = false; if (requestListeners != null) { for (RequestListener listener : requestListeners) { anyListenerHandledUpdatingTarget |= listener.onResourceReady(result, model, target, dataSource, isFirstResource); } } anyListenerHandledUpdatingTarget |= targetListener != null && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource); if (!anyListenerHandledUpdatingTarget) { Transition animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation); } } finally { isCallingCallbacks = false; } notifyLoadSuccess(); } // ... }
回调的是第一个 onResourceReady()
方法,我们可以看到 Object received = resource.get()
获得了我们刚才封装的 BitmapDrawable 对象;然后将这个值传入到了第二个 onResourceReady()
方法当中,并调用了 target.onResourceReady(result, animation)
ImageViewTarget#onResourceReady(Z resource, Transition transition)
public abstract class ImageViewTarget extends ViewTarget implements Transition.ViewAdapter { // ... @Override public void onResourceReady(@NonNull Z resource, @Nullable Transition 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); // ... }
DrawableImageViewTarget#setResource(Drawable resource)
public class DrawableImageViewTarget extends ImageViewTarget { public DrawableImageViewTarget(ImageView view) { super(view); } /** @deprecated Use {@link #waitForLayout()} instead. */ // Public API. @SuppressWarnings({"unused", "deprecation"}) @Deprecated public DrawableImageViewTarget(ImageView view, boolean waitForLayout) { super(view, waitForLayout); } @Override protected void setResource(@Nullable Drawable resource) { view.setImageDrawable(resource); } }
在这里把 drawable 塞给了 ImageView