You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/* * @param context Any context, will not be retained. * @return A RequestManager for the top level application that can be used to start a load. * @see #with(android.app.Activity) * @see #with(android.app.Fragment) * @see #with(android.support.v4.app.Fragment) * @see #with(android.support.v4.app.FragmentActivity) */@NonNullpublicstaticRequestManagerwith(@NonNullContextcontext) {
returngetRetriever(context).get(context);
}
@NonNullpublicstaticRequestManagerwith(@NonNullViewview) {
returngetRetriever(view.getContext()).get(view);
}
// 最终调用@NonNullprivatestaticRequestManagerRetrievergetRetriever(@NullableContextcontext) {
// 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).");
returnGlide.get(context).getRequestManagerRetriever();
}
@NonNullpublicstaticGlideget(@NonNullContextcontext) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
// 初始化 glidecheckAndInitializeGlide(context);
}
}
}
returnglide;
}
privatestaticvoidcheckAndInitializeGlide(@NonNullContextcontext) {
// 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) {
thrownewIllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
// 初始化initializeGlide(context);
isInitializing = false;
}
/** * A factory responsible for producing the correct type of * {@link com.bumptech.glide.request.target.Target} for a given {@link android.view.View} subclass. */publicclassImageViewTargetFactory {
@NonNull@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNullImageViewview,
@NonNullClass<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) newBitmapImageViewTarget(view);
} elseif (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) newDrawableImageViewTarget(view);
} else {
thrownewIllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
接着经过 into 的几个重载方法,
private <YextendsTarget<TranscodeType>> Yinto(
@NonNullYtarget,
@NullableRequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
ExecutorcallbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
thrownewIllegalArgumentException("You must call #load() before calling #into()");
}
// 构建新的请求Requestrequest = buildRequest(target, targetListener, options, callbackExecutor);
// 老请求Requestprevious = 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();
}
returntarget;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
returntarget;
decodeJob 将会将会开始执行,并将结果回调给 cb。因为 DecodeJob 继承了 Runnable, 所以会执行其 run 方法。
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
publicsynchronizedvoidstart(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutorexecutor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
publicvoidrun() {
// 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 (CallbackExceptione) {
// If a callback not controlled by Glide throws an exception, we should avoid the Glide// specific debug logic below.throwe;
} catch (Throwablet) {
// 主要过程在上面的 runWrapper(), 这里是为了捕获和处理 OOM 异常,图片操作不可避免。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) {
throwt;
}
throwt;
} 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();
}
@OverridepublicvoidonDataReady(Objectdata) {
DiskCacheStrategydiskCacheStrategy = 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);
}
}
Glide:The loading process of the main flow
[TOC]
分析来自 glide 4.8.0, 官方 使用文档
上面是官网介绍的最简单的用法,本文章就对这个主流程做个简单分析。
.with()
首先来看 with 方法做了什么:
with 有多个重载方法,最终都会调用上述方法, 最终返回一个 RequestManager 对象。这里传入的 context 必须要绑定 view,或者 fragment.getActivity() 不等于 null 。
下面重点来看一下 初始化方法中具体做了什么:
所以知道 Glide类中的属性大概有些什么,其中重要的资源池,请求和缓存,后续会介绍。
回到初始化的逻辑,内容有点长,在关键部分做了注释。
上面的配置中,MemorySizeCalculator 内存大小的计算比较重要。至此,生成了 glide,用作后续处理的单例。
接着返回一个 requestManagerRetriever, 看一下glide 的属性,大部分都在 builder 中见过。
继续往下,
getRetriever(context).get(context);
然后分别 get 不同对象获取 RequestManager。这里创建了一个无UI的SupportRequestManagerFragment, 使其内部的 RequestManager 可以结合生命周期,安全的开始,停止和管理 glide 请求。这种方法在很多地方都有用到,不明白可以继续看一下源码。
小结
在上面的分析中,做的工作主要有:
各种属性的初始化工作,创建 glide 单例 —> 获取请求管理 RequestManager对象, 同时绑定 Lifecycle, start,stop,destory 事件。
有了 RequestManager 对象,那么接下来可以看一下 load 方法做了什么。
.load()
也是有多个重载方法,基本支持所有的图片资源加载,看下面的注释;
通过调用 as(Drawable.class) 将传入的类型 decode 为 Drawable 对象。
下面就开始 进入 load 步骤, 无论调用 load 的哪个重载方法,都会进入下面的代码:
这一步将需要加载的 对象 赋值给RequesBuild 的 model 对象,接着就到了最复杂 into 步骤。
.into()
该方法接收一个 ImageView 对象,将要加载的图片在 view 上显示出来。
加载图片的过程分为几步,下面分别从每一步开始分析。
into() 也是有好几个重载方法,在拿到 ImageView 对象之后,会调用以下方法生成 target 对象,这里就是
DrawableImageViewTarget 对象。
接着经过 into 的几个重载方法,
第一阶段
在构建 新的请求中,最后会调用到此方法, 主要的注释如下:
上面的配置都是 glide 支持的配置,支持 error 图片和 缩略图 显示。同样,主流程没有设置,那么最终 创建一个 SingleRequest 对象。
到目前有了请求和target ,
requestManager.track(target, request);
进行 生命周期管理,以及开始进行请求当配置的大小确定, 则进入 onSizeReady(), 方法,接着会调用 Engine.load() 方法,返回一个 LoadStatus 对象.
该方法使用给定的参数开始加载,大概的流程如下:
检查当前使用的活跃资源,如果存在则使用;并且将新的不活跃的资源加入内存缓存;
如果内存缓存中存在,是直接使用;
检查正在加载的资源中是否存在,如果存在则返回;
否则开始新的加载。
注:这里的活跃资源是指至少提供给一个请求并且还没有为回收的资源。如果所有使用者都释放了资源,将会进入缓存;这时如果从缓存中被重新使用,那么又会加入活跃资源中;如果资源从缓存中移除,那么资源会被重新利用,也可能被丢弃。没有严格要求释放资源,所以活跃的资源都很不稳定。
上面方法中涉及两个类,一个是
DecodeJob
、一个是EngineJob
。它们之间的关系是,EngineJob
内部维护了线程池,用来管理资源加载,已经当资源加载完毕的时候通知回调。DecodeJob
继承了Runnable
,是线程池当中的一个任务。就像上面那样,我们通过调用engineJob.start(decodeJob)
来开始资源加载。####小结
在第一阶段中,整体还是比较清晰。 首先创建的 SingleRequest,然后 RequestMangenr 对 target 和 request 进行管理, 生命周期和加入请求队列;
接着开始 request,会调用 Engine.load() 方法来决定从活跃资源,或者缓存,或者新建 DecodeJob 中加载资源。
第二阶段
decodeJob 将会将会开始执行,并将结果回调给 cb。因为 DecodeJob 继承了 Runnable, 所以会执行其 run 方法。
DecodeJob
的执行过程使用了状态模式,它会根据当前的状态决定将要执行的方法。在上面的方法中,当当前任务没有被取消的话,会进入到runWrapped()
方法。该方法中会使用runReason
作为当前的状态决定要执行的逻辑:这里的
runReason
是一个枚举类型,它包含的枚举值即为上面的三种类型。当我们在一个过程执行完毕之后会回调DecodeJob
中的方法修改runReason
,然后根据新的状态值执行新的逻辑。除了
runReason
,DecodeJob
中还有一个变量stage
也是用来决定DecodeJob
状态的变量。同样,它也是一个枚举,用来表示将要加载数据的数据源以及数据的加载状态。它主要在加载数据的时候在runGenerators()
、runWrapped()
和getNextStage()
三个方法中被修改。通常它的逻辑是,先从(大小、尺寸等)转换之后的缓存中拿数据,如果没有的话再从没有转换过的缓存中拿数据,最后还是拿不到的话就从原始的数据源中加载数据。以上就是
DecodeJob
中的状态模式运行的原理。在上面的逻辑中,从
INITIALIZE
状态开始,由于这是没有设计到缓存,所以 得到SourceGenerator
,由于这是加载的是网络上的资源,默认情况下会使用
HttpUrlFetcher
下载数据:小结
这里涉及到 DecodeJob 的执行,和网络的请求过程,代码量相对较大的部分。因此也只是一个简单的分析,多看源码,多思考。下一阶段是将输入流转换成 Drawable。
第三阶段
上面的阶段结束,会回调 onDataReady() 方法:
glide 有默认磁盘缓存策略,所以这里会执行:
cb.reschedule();
, 最后又回到 DecodeJob 的run 方法。这里的主要逻辑是构建一个用于将数据缓存到磁盘上面的
DataCacheGenerator
。DataCacheGenerator
的流程基本与SourceGenerator
一致,也就是根据资源文件的类型找到ModelLoader
,然后使用DataFetcher
加载缓存的资源。与之前不同的是,这次是用DataFecher
来加载File
类型的资源。也就是说,当我们从网络中拿到了数据之后 Glide 会先将其缓存到磁盘上面,然后再从磁盘上面读取图片并将其显示到控件上面。所以,当从网络打开了输入流之后SourceGenerator
的任务基本结束了,而后的显示的任务都由DataCacheGenerator
来完成。DataCacheGenerator
中使用DataUrlLoader.loadData()
后,回调onDataReady()
方法,接着会调用到:这一阶段执行完毕,主要是 获取输入流之后的 处理过程,到达下一阶段。
第四阶段
notifyEncodeAndRelease(resource, currentDataSource);
方法一路往下追,最后回到
SingleRequest
,然后 回调 ImageViewTarget 的方法, 整个过程结束。
总结
以上就是
Glide.with(fragment).into(ImageView)
的主流程的分析,大致步骤就是初始化glide
,创建RequestManager
, 接着创建出加载的类型RequestBuild<Drawable>
的model
对象。在
into
过程中,主要有以下几步:第一个阶段是开启
DecodeJob
的过程。DecodeJob
负责从缓存或者从原始的数据源中加载图片资源,对图片进行变换和转码,是 Glide 图片加载过程的核心。DecodeJob
继承了Runnable
,实际进行图片加载的时候会将其放置到线程池当中执行。这个阶段我们重点介绍的是从RequestBuilder
构建一个DecodeJob
并开启DecodeJob
任务的过程。即构建一个DecodeJob
并将其丢到线程池里的过程。第二个阶段是打开网络流的过程。这个阶段会根据我们的图片资源来从数据源中加载图片数据。以我们的示例为例,在默认情况下会从网络当中加载图片,并得到一个
InputStream
.第三个阶段是将输入流转换为
Drawable
的过程。得到了InputStream
之后还要调用BitmapFactory
的decodeStream()
方法来从InputStream
中得到一个Drawable
.第四个阶段是将
Drawable
显示到ImageView
上面的过程。其中最重要并且最负责的是中间两步,这篇文章只是提了个大概,后续会详细的分析。
当然,关于 Glide 还有很多没有谈到, 包括可配置的 RequestOptions,缓存,注解时生成的部分,希望后面有时间慢慢从用法到源码,慢慢了解吧。
参考文章
官方中文文档翻译
Glide 系列-2:主流程源码分析(4.8.0)
The text was updated successfully, but these errors were encountered: