Glide是Android开发中常用的图片框架,其最基本用法例如Glide.with(context).load(url).into(imageView),我们沿着此链式调用的顺序一窥Glide图片加载流程的样貌。

一图胜千言

1.Glide.with(context)

// Glide.java
public static RequestManager with(@NonNull Context context) { return getRetriever(context).get(context);
}
public static RequestManager with(@NonNull Activity activity) { return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) { return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) { return getRetriever(fragment.getContext()).get(fragment);
}
public static RequestManager with(@NonNull View view) { return getRetriever(view.getContext()).get(view);
}

该函数的作用是创建了RequestManager对象并返回。其参数有五种类型,这里选取Context类型情形进行分析。

可以看到RequestManager对象是先通过getRetriver()方法创建得到一个RequestManagerRetriever对象,再调用该对象的get()方法返回。

1.1RequestManager对象的创建过程

// Glide.java
private static RequestManagerRetriever getRetriever(@Nullable Context context) { // Glide.get(context)获取Glide实例 return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) { if (glide == null) { // 加载AppGlideModule GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext()); synchronized (Glide.class) { if (glide == null) { // 加载Mainfest配置、注册模块回调 // 这一步执行了 Glide.build()方法构造Glide实例。build方法下面会讲到 checkAndInitializeGlide(context, annotationGeneratedModule); } } } return glide; }

getRetriever(context)方法中,先通过Glide.get(context)获取Glide实例,Glide采用双重校验锁的懒汉式的单例模式创建,同时读取AppGlideModule和AndroidManifest.xml的配置。

获取到Glide实例后,紧接着调用getRequestManagerRetriever方法返回了上一步已经初始化好的RequestManagerRetriever对象。

1.1.1RequestManagerRetriever是如何初始化的?

RequestManagerRetriever对象的初始化主要是在Glide对象的创建过程中完成的,也就是Glide.get(context)方法中的checkAndInitializeGlide()函数中,该函数内部调用了Glide.build方法,源码如下:

// GlideBuilder.java
Glide build(@NonNull Context context) { // 分配线程池、配置缓存策略 sourceExecutor = GlideExecutor.newSourceExecutor(); diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); animationExecutor = GlideExecutor.newAnimationExecutor(); memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); // 监听网络变化 connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); int size = memorySizeCalculator.getBitmapPoolSize(); if (size > 0) { bitmapPool = new LruBitmapPool(size); } else { bitmapPool = new BitmapPoolAdapter(); } arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); diskCacheFactory = new InternalCacheDiskCacheFactory(context); // engine是负责执行加载任务的 if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), animationExecutor, isActiveResourceRetentionAllowed); } if (defaultRequestListeners == null) { defaultRequestListeners = Collections.emptyList(); } else { defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); } // focus 1===RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory); return new Glide( context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel, defaultRequestOptionsFactory, defaultTransitionOptions, defaultRequestListeners, isLoggingRequestOriginsEnabled, isImageDecoderEnabledForBitmaps); }

注意focus 1处的代码创建了RequestManagerRetriever对象。

public interface RequestManagerFactory {@NonNullRequestManager build(@NonNull Glide glide,@NonNull Lifecycle lifecycle,@NonNull RequestManagerTreeNode requestManagerTreeNode,@NonNull Context context);}

从以上代码我们可以知道:

1.执行Glide.get()方法时就已经分配好了资源加载、缓存线程池、配置好了缓存策略,这里的engine专门负责加载、解码资源,ConnectivityMonitor注册了网络状态监听器,当网络断开时暂停请求网络资源,重连后继续请求资源;
2.RequestManagerRetriever是通过RequestManagerFactory工厂类构造的,进入到RequestManagerFactory.java类中,可以看到其build()方法获取到了相应的RequestManager对象;

1.1.2 factory.build()方法在什么时候调用的?

在之前getRetriever(context).get(context);的调用中可以发现
在RequestManagerRetriever类中有一个get()方法,正是在这个方法中调用了factory.build()方法返回了RequestManager对象。

如果Glide.with(context)传入的context参数是Application类或者是在后台线程调用的该方法,则调用getApplicationManager(Context context)

private RequestManager getApplicationManager(@NonNull Context context) {// Either an application context or we're on a background thread.if (applicationManager == null) {synchronized (this) {if (applicationManager == null) {// Normally pause/resume is taken care of by the fragment we add to the fragment or// activity. However, in this case since the manager attached to the application will not// receive lifecycle events, we must force the manager to start resumed using// ApplicationLifecycle.// TODO(b/27524013): Factor out this Glide.get() call.Glide glide = Glide.get(context.getApplicationContext());applicationManager =factory.build(glide,new ApplicationLifecycle(),new EmptyRequestManagerTreeNode(),context.getApplicationContext());}}}return applicationManager;}

否则调用supportFragmentGet或者fragmentGet

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;}

从这里也可以发现,当App进入后台后会导致页面不可见,此时RequestManager绑定到了ApplicationContext,与App的生命周期一致;在其他情况下,RequestManager是和当前页面的生命周期绑定,因此在RequestManager.java类中也实现了生命周期相关的回调函数;

执行完Glide.with(context)后我们拿到了一个对应的RequestManager对象,接下来就执行下一个任务load(url);

load(url)

拿到了RequestManager,紧接着调用load方法开始执行下一步操作,同样先看看load方法的实现

// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) { return asDrawable().load(bitmap); } public RequestBuilder<Drawable> load(@Nullable Drawable drawable) { return asDrawable().load(drawable); } public RequestBuilder<Drawable> load(@Nullable String string) { return asDrawable().load(string); } public RequestBuilder<Drawable> load(@Nullable Uri uri) { return asDrawable().load(uri); } public RequestBuilder<Drawable> load(@Nullable File file) { return asDrawable().load(file); } public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) { return asDrawable().load(resourceId); } public RequestBuilder<Drawable> load(@Nullable URL url) { return asDrawable().load(url); } public RequestBuilder<Drawable> load(@Nullable byte[] model) { return asDrawable().load(model); } public RequestBuilder<Drawable> load(@Nullable Object model) { return asDrawable().load(model); } 

load()同样有多个重载函数,传入的参数可以是图片对象Bitmap、Drawable、本地资源Uri、在线资源路径Url、文件对象File、assets资源的id,这里以参数为Url为例;

asDrawable().load(url)返回了一个RequestBuilder对象,首先看看asDrawable方法干了什么;

// RequestManager.java public RequestBuilder<Drawable> asDrawable() { return as(Drawable.class); } public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide, this, resourceClass, context); }

asDrawable方法创建了RequestBuilder对象,然后调用RequestBuilder.java中的load方法;

// RequestBuilder.java // 传入的String类型的url将会被作为缓存的key public RequestBuilder<TranscodeType> load(@Nullable String string) { return loadGeneric(string); } // 这里返回了自身 private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; } 

load函数主要工作就是根据传入的资源类型,构造了一个相应的RequestBuilder对象。至此一切准备工作准备就绪,接下来就是最为重要的一步了-加载、展示文件,让我们来着看into(view)方法如何完成这些任务;

3.load(imageView)

拿到的是对应类型RequestBuilder实例,那么就看看该类里into方法的具体实现。同样into方法有into(@NonNull Y target)into(@NonNull ImageView )两个重载函数(这两个函数最终都会走到同一个函数中),由于调用into方法时我们传入的参数是ImageView类型的;

// RequestBuilder.java public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { Util.assertMainThread(); BaseRequestOptions<?> requestOptions = this; // View's scale type. // 处理图片缩放,根据缩放类型来初始化对应的requestOptions对象 ...... return into( glideContext.buildImageViewTarget(view, transcodeClass), /*targetListener=*/ null, requestOptions, Executors.mainThreadExecutor() // 运行在主线程的handler ,利用Handler实现子线程到主线程的切换,因为最终将图片设置到ImageView是需要在主线程操作的); }

上面代码段首先处理图片缩放类型(裁剪、对齐方式等),并将生成的相关参数放入了requestOptions对象中,然后再将其作为参数传给了RequestBuilder.java类私有方法into。该方法定义的四个参数分别为:viewTarget、target回调监听器、请求参数、主线程的回调函数;

显然外部传入ImageView对象最终被转换成了ViewTarget对象,转换函数便是glideContext.buildImageViewTarget(view, transcodeClass);
无论传入参数是何种类型,最终都会转换成两种类型的ViewTarget :BitmapImageViewTarget或者DrawableImageViewTarget;具体取决于asBitmap()、asGif()和asDrawable()函数是否被调用,默认是调用的asDrawable(),所以默认返回的是DrawableImageViewTarget类型。

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {public DrawableImageViewTarget(ImageView view) {super(view);}/** @deprecated Use {@link #waitForLayout()} instead. */// Public API.@SuppressWarnings({"unused", "deprecation"})@Deprecatedpublic DrawableImageViewTarget(ImageView view, boolean waitForLayout) {super(view, waitForLayout);}@Overrideprotected void setResource(@Nullable Drawable resource) {view.setImageDrawable(resource); //显示图片}
}

至此ViewTarget创建完毕,我们再回到RequestBuilder.java私有into方法

// RequestBuilder.java` private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { // 注释1:创建request Request request = buildRequest(target, targetListener, options, callbackExecutor); // 获取前一个reqeust请求对象 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; } 

顺着代码次序,来看看这个方法每一步都干了什么:

1.首先执行buildRequest方法创建一个新的Request请求req1;
2.获取当前ViewTarget上正在进行中的Request请求req2;
3.判断新建的请求req1与已有的请求req2是否相同,如果相同则判断是否跳过req2请求的缓存,两个条件都满足则开始执行begin()方法开始请求资源并停止往下执行,条件都不满足则继续执行第四步;
4.给ViewTarget设置最新的请求req1,然后执行track方法追踪req1。
5.执行into(view)方法首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();

当我们在常见的列表界面中(如 recycleview 实现的列表),使用上面的代码,在我们快速滑动中,glide 是如何实现正确加载图片,而没有导致图片内容的错位或者是不正确呢?

答案就在上面的代码中。执行上面的代码后,glide 要把最新的图片加载到正确的对象上,而取消对象之前关联的图片加载请求。

首先,在该方法中构建了一个 Request 并持有了 target 。这样便可以将结果通知给 target。Target 是 Glide 对我们要加载目标的一个封装和抽象。
判断Taget 中的之前的 Request 和最新构建的 Request 是否相同,如果相同回收最新的 Request ,让旧的 Request 继续运行。如果不同,就取消之前的 Request 和 target 的关联。

 requestManager.clear(target);

最终会触发下面的代码

if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {Request request = target.getRequest();target.setRequest(null);request.clear();}

可以看到, target 对应的 request 被置 null, 而旧的 request 被 “clear”。旧的 Request 被 clear 后,又是如何让资源没有去加载到关联的 Target 上的? 我们看其中 SingleRequest 的实现

# SingleRequest.java/*** Cancels the current load if it is in progress, clears any resources held onto by the request* and replaces the loaded resource if the load completed with the placeholder.** <p> Cleared requests can be restarted with a subsequent call to {@link #begin()} </p>** @see #cancel()*/@Overridepublic void clear() {Util.assertMainThread();assertNotCallingCallbacks();stateVerifier.throwIfRecycled();if (status == Status.CLEARED) {return;}cancel();// Resource must be released before canNotifyStatusChanged is called.if (resource != null) {releaseResource(resource);}if (canNotifyCleared()) {target.onLoadCleared(getPlaceholderDrawable());}// Must be after cancel().status = Status.CLEARED;}

可以看到 clear() 方法中先是 执行了 cancel(),该方法会取消加载资源请求与该 Request 的回调关联。

  /*** Cancels the current load but does not release any resources held by the request and continues* to display the loaded resource if the load completed before the call to cancel.** <p> Cancelled requests can be restarted with a subsequent call to {@link #begin()}. </p>** @see #clear()*/void cancel() {assertNotCallingCallbacks();stateVerifier.throwIfRecycled();target.removeCallback(this);status = Status.CANCELLED;if (loadStatus != null) {loadStatus.cancel();loadStatus = null;}}

起关键作用代码为

 loadStatus.cancel();
public static class LoadStatus {private final EngineJob<?> engineJob;private final ResourceCallback cb;LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {this.cb = cb;this.engineJob = engineJob;}public void cancel() {engineJob.removeCallback(cb);}}

LoadStatus 实际上只是持有了ResourceCallback回调(该回调可获取资源加载成功或失败)和 EngineJob。
EngineJob 是负责加载资源,并在加载成功后回调回去,这里 SingleRequest 实现了回调,所以它便可得知资源加载完成并获取到。所以 cancel() 调用后,即使旧的加载请求完成也不会回调到 Tareget 上。

  target.setRequest(request);requestManager.track(target, request);

在该方法中,方法中,Target 持有了最新的 request , requestManager.track() 方法则触发了 request 的加载请求,实际是由内部 Engine 和 EngineJob 负责。当顺利加载成功后便回调到 Target 对象上,触发 target.onResourceReady(result, animation) 方法,图片便被正确显示出来了。

###########################################################

回到上面的流程,在into(view)方法中,首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();

// ReqeustManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) { // 与lifecycle绑定 targetTracker.track(target); // 启动reqeust requestTracker.runRequest(request); }
// RequestTracker.java public void runRequest(@NonNull Request request) { requests.add(request); if (!isPaused) { request.begin(); // 立即开始加载 } else { //防止从以前的请求中加载任何位图,释放该请求所拥有的任何资源,显示当前占位符(如果提供了该占位符),并将该请求标记为已取消。 // request.java( Interface ) request.clear(); pendingRequests.add(request); // 加入队列等待执行 } }

Request类是interface类型,begin()是它的抽象方法,要想弄清楚begin()的具体实现,那就要先找到Request的实现类,SingleRequest,我们直接去SingleReqeust类里面 看看begin方法如何实现的;

// SingleReqeust.java
public void begin() { if (status == Status.COMPLETE) { // 资源已下载,直接回调 // 执行动画 onResourceReady(resource, DataSource.MEMORY_CACHE); return; } // 计算尺寸 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()); } }

进入begin方法后首先判断如果资源已经过加载好了则直接回调onResourceReady显示图片并缓存,否则测量出图片尺寸后再开始加载图片(onSizeReady()中执行加载任务)并同时显示占位图:
接着再看onSizeReady()测量完图片尺寸后如何加载图片的:

// SingleRequest.java
@Override public void onSizeReady(int width, int height) { if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; // 获取图片尺寸 float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); // 开始加载任务 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); } 

可以看到真正的下载任务是在Engine类的load方法中实现的,其中也涉及到了图片缓存逻辑;

最终通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上;
具体再分析一下线程是如何切换的。

线程切换的过程

回到SingleRequest.onSizeReady()

# SingleRequest
@Overridepublic void onSizeReady(int width, int height) {...// 省略不相关代码...
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);
}
#Engine
public <R> LoadStatus load(GlideContext glideContext,Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//focus1 更新相应属性创建缓存key--EngineKey
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);
//尝试从活跃的资源内存缓存中中获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {cb.onResourceReady(active, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;
}
//尝试从内存缓存中获取
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {cb.onResourceReady(cached, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;
}
//判断当前请求是在在队列中
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {current.addCallback(cb);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);
}
//创建请求job
EngineJob<R> engineJob =engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);
//创建解码job
DecodeJob<R> decodeJob =decodeJobFactory.build(glideContext,model,key,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,onlyRetrieveFromCache,options,engineJob);
//保存当前请求job
jobs.put(key, engineJob);engineJob.addCallback(cb);
//开始请求
engineJob.start(decodeJob);return new LoadStatus(cb, engineJob);
}

注意focus1处的代码

假设一个ImageView的宽高是300300,另一个ImageView的宽高是900900,则在这两个ImageView中加载同一张图片资源会缓存几次?

创建图片缓存Key和图片的model(图片链接Url)、图片资源的宽高都有关系,因此两个不同宽高的ImageView加载同一图片会缓存两次。

1.尝试从活跃的资源内存缓存中中获取资源,取到直接回调返回,没取到执行第二步
2.尝试从内存缓存中获取资源,取到直接回调返回,没取到执行第三步
3.创建请求engineJob
4.执行请求

#EngineJob
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
// focus1
GlideExecutor executor = decodeJob.willDecodeFromCache()? diskCacheExecutor: getActiveSourceExecutor();
executor.execute(decodeJob);
}

DecodeJob实现了Runnable接口,这里将其添加到线程池中,接下来看下run方法 。

#DecodeJob
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {//判断是否已经被取消if (isCancelled) {notifyFailed();return;}runWrapped();
} catch (Throwable t) {if (stage != Stage.ENCODE) {throwables.add(t);notifyFailed();}if (!isCancelled) {throw t;}
} finally {if (localFetcher != null) {localFetcher.cleanup();}GlideTrace.endSection();
}}
private void runWrapped() {
switch (runReason) {case INITIALIZE:stage = getNextStage(Stage.INITIALIZE);//根据当前流程步骤获取相应获取数据类(刚开始肯定为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);
}
}
# DecodeJob.getNextGeneratorprivate 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);
}
}

默认的AUTOMATIC方式是允许解码缓存的RESOURCE,所以在 getNextStage 会先返回Stage.RESOURCE_CACHE,然后在start中会返回diskCacheExecutor,然后开始执行DecodeJob,也就是说这里会获得一个ResourceCacheGenerator,

#DecodeJob.runGenerators()private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;while (!isCancelled && currentGenerator != null&& !(isStarted = currentGenerator.startNext())) {stage = getNextStage(stage);currentGenerator = getNextGenerator();if (stage == Stage.SOURCE) {reschedule();return;}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {notifyFailed();
}}

在 runGenerators 中,会调用 startNext,目前currentGenerator是ResourceCacheGenerator, 那么就是调用它的startNext方法:

@Overridepublic boolean startNext() {...# 省略部分代码...// 这里会从registry中匹配相应的Modeloader来加载数据loadData.fetcher.loadData(helper.getPriority(), this);...return started;}

这里是loadData.fetcher是HttpUrlFetcher。

# HttpUrlFetcher.loadData()
@Override
public void loadData(@NonNull Priority priority,@NonNull DataCallback<? super InputStream> callback) {
// 省略部分代码//连接网络获取图片流InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());//成功回调callback.onDataReady(result);
}

请求成功之后回调了DataCallback方法。最终会回调到DecodeJob的onDataFetcherReady方法,之后在进行解码操作:
解码成功之后,回调EngineJob中的onResourceReady方法:

# EngineJobpublic void onResourceReady(Resource<R> resource, DataSource dataSource) {synchronized (this) {this.resource = resource;this.dataSource = dataSource;}notifyCallbacksOfResult();}

在notifyCallbacksOfResult方法中:

# Engine.java
void notifyCallbacksOfResult() {// 省略部分代码engineJobListener.onEngineJobComplete(this, localKey, localResource);for (final ResourceCallbackAndExecutor entry : copy) {// focus entry.executor.execute(new CallResourceReady(entry.cb));}decrementPendingCallbacks();}

注意focus行的代码:

使用entry中的executor执行线程,而entry就是ResourceCallbackAndExecutor ,而它里面的executor正好是我们Executors.mainThreadExecutor()

很明显它是在这里切换到了主线程。

补充说明1:

首先回顾下主线程的创建,在RequestBuilder.java中的into方法中:

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {Util.assertMainThread();...return into(glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions,Executors.mainThreadExecutor());}Executor.javaprivate static final Executor MAIN_THREAD_EXECUTOR =new Executor() {private final Handler handler = new Handler(Looper.getMainLooper());@Overridepublic void execute(@NonNull Runnable command) {handler.post(command);}};

它最终会一层一层传递到EngineJob中。engineJob.addCallback(cb, callbackExecutor); 它将被实例化为一个ResourceCallbackAndExecutor对象并add到cbs集合中。

EngineJob.javasynchronized void addCallback(final ResourceCallback cb, Executor callbackExecutor) {...cbs.add(cb, callbackExecutor);...}void add(ResourceCallback cb, Executor executor) {callbacksAndExecutors.add(new ResourceCallbackAndExecutor(cb, executor));}

补充说明2:

以上代码基于glide 4.11.0版本,可能代码有所不同,但是原理是一样。

参考:
Android源码进阶之Glide加载流程和源码详解

Glide图片加载流程浅析相关推荐

  1. Glide 4.9源码解析-图片加载流程

    本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 由于Glide源码真的很复杂,因此本文只分析和贴出与图片加载流程相关的功能以及代码.另外本文Glide源码基于4.9,与3.x ...

  2. Android Glide图片加载框架(二)源码解析之into()

    文章目录 一.前言 二.源码解析 1.into(ImageView) 2.GlideContext.buildImageViewTarget() 3.RequestBuilder.into(Targe ...

  3. 从源码分析Android的Glide库的图片加载流程及特点

    转载:http://m.aspku.com/view-141093.html 这篇文章主要介绍了从源码分析Android的Glide库的图片加载流程及特点,Glide库是Android下一款人气很高的 ...

  4. Android Glide图片加载框架(三)缓存机制

    文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...

  5. Android Glide图片加载框架(二)源码解析之with()

    文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...

  6. Glide图片加载框架的使用

    1. 介绍 Glide是一个快速高效的Android图片加载库,注重于平滑的滚动.Glide提供了易用的API,高性能.可扩展的图片解码管道(decode pipeline),以及自动的资源池技术.G ...

  7. Android Glide图片加载框架(四)回调与监听

    文章目录 Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源码解析之with() Android Gl ...

  8. Android Glide图片加载框架(二)源码解析之load()

    文章目录 一.前言 二.源码分析 1.load() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图片加载框架(二)源 ...

  9. Android Glide图片加载框架(一)基本用法

    文章目录 一.前言 二.简介 三.基本用法 第一步:调用 Glide.with() 方法创建加载图片的实例 第二步:调用 load() 方法指定待加载的图片资源 第三步:调用 into() 方法绑定显 ...

最新文章

  1. jdbc mysql查询显示图片_JDBC【向数据库中存入读取图片】
  2. php 检测是否是微信浏览器,php判断是否是微信浏览器和是否是移动端代码
  3. 使用ffmpeg实现转码样例(代码实现)
  4. 指针:调用自定义排序函数sort,对输入的n个数进行从小到大输出。
  5. 如何删除 Windows.old 文件夹
  6. 115. 不同的子序列(JavaScript)
  7. couchdb 视图操作_CouchDB 教程
  8. 淘宝API签名异常,如何正确计算SIGN参数?(error code:25 Invalid Signature)
  9. 全新的服务器debian/ubuntu---校准时间、更新apt,设置ssh远程访问
  10. 网络连接正常,浏览器确没有网?
  11. android 强制竖屏
  12. 阿里巴巴-数据平台事业部-数据产品部:寻找真爱
  13. POBPM集成-数据过滤
  14. 如何通过自学,成为数据挖掘“高手”
  15. 信用评分卡 (part 5 of 7)
  16. 【思维进阶】新手小白如何练习写作
  17. 速联2.0 实现SCADA软件远程无线监控环保设备
  18. 京东面经 10.10
  19. 浅析镜头机身跑焦原因与解决办法
  20. 【渝粤教育】国家开放大学2018年春季 4972T农业项目投资 参考试题

热门文章

  1. linux mysql 多表联合_MySQL的多表联查
  2. Unity 3D 地形系统概述|| Unity 3D 创建地形
  3. 毕业论文系统的设计类图
  4. BZOJ2744洛谷P2423P2423 [HEOI2012]朋友圈
  5. 使用 matlab 心电数据捕获心率
  6. 【广告算法】基础知识补充
  7. CANopen / PROFIBUS DP网关
  8. Mysql中查询当前用户、当前数据库等基础信息
  9. ovirt几种网卡(e1000, rtl8139,virtio)
  10. java面试宝典2019(总结)