??大家应该都听说过Glide图片加载库,或多或少都知道怎么去使用Glide去加载一张图片。Glide对于使用者来说是非常友好的一个库。那么今天我们就来学习Glide的源码,网上也有很有优秀的博客讲解Glide的源码的,现在我们一起来学习Glide的源码。这个优秀的框架是怎么帮助我们实现图片的加载和显示的。很多人看源码都不知道从哪里下手。这篇博客也分享一下我平时是怎么看源码的。这是基于Glide:4.10 版本的源码分析
??首先我们平时是怎么使用Glide的?
Glide.with(context).load(url).into(iv);
这就是平时使用Glide的代码,那么看源码也很简单,就直接从这一段代码开始看,这段代码有三个部分,这三个部分分别是with,load和into。我们这个分析源码也一步一步分析这三个步骤。下面就来看看with,load,into分别做了什么。
Glide.with(context)
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
我们点进去Glide的源码看一下,看with这个函数,它有很多重载函数,但是他们里面都有一个相同的步骤就是调用getRetriever(context).get(context) 。
那么接下来继续跟下去,因为通过getRetriever(context).get(context) 我们并不知道里面是什么,看源码的话,就是要知道这里面究竟做了什么。这个方法有两个调用,首先先看getRetriever()
Glide#getRetriever
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
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();
}
这个方法也有两个调用,他里面真正重要的是 Glide.get(context) 。因为getRequestManagerRetriever() 只是返回一个RequestManagerRetriever 而已。所以接下来我们要重点关注了就是Glide.get(context)
Glide#get(context)
@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;
}
@GuardedBy("Glide.class")
private static void checkAndInitializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
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;
}
@GuardedBy("Glide.class")
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
}
iterator.remove();
}
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
}
}
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
try {
module.registerComponents(applicationContext, glide, glide.registry);
} catch (AbstractMethodError e) {
throw new IllegalStateException(
"Attempting to register a Glide v3 module. If you see this, you or one of your"
+ " dependencies may be including Glide v3 even though you're using Glide v4."
+ " You'll need to find and remove (or update) the offending dependency."
+ " The v3 module name is: "
+ module.getClass().getName(),
e);
}
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
如果只用Glide.with(context).load(url).into(iv); 其实上面的initializeGlide 方法可以简化成如下:
Glide#initializeGlide(简化)
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
Glide glide = builder.build(applicationContext);
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
所以接下来的目的就清晰了就是 builder.build(applicationContext) 是如何创建glide的。builder 是在initializeGlide(context, new GlideBuilder(), generatedAppGlideModule) 的时候,自己创建的一个GlideBuilder
GlideBuilder#build
Glide build(@NonNull Context context) {
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
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);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps);
}
现在就到了Glide的构造函数了,整一个Glide的构造方法代码很多,这里会精简一下代码,然后介绍各个部分的作用
Glide#Glide
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptionsFactory defaultRequestOptionsFactory,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
boolean isLoggingRequestOriginsEnabled,
boolean isImageDecoderEnabledForBitmaps) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;
final Resources resources = context.getResources();
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
ContentResolver contentResolver = context.getContentResolver();
registry
.append(...)
.register(...)
...
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
下面讲解一下Registry 对象,调用append,register 方法的作用。首先设置一些参数。这些参数,主要作用就是对后面的图片解析、加载。
看一下Register的构造函数
Registry#Registry
public Registry() {
this.modelLoaderRegistry = new ModelLoaderRegistry(throwableListPool);
this.encoderRegistry = new EncoderRegistry();
this.decoderRegistry = new ResourceDecoderRegistry();
this.resourceEncoderRegistry = new ResourceEncoderRegistry();
this.dataRewinderRegistry = new DataRewinderRegistry();
this.transcoderRegistry = new TranscoderRegistry();
this.imageHeaderParserRegistry = new ImageHeaderParserRegistry();
setResourceDecoderBucketPriorityList(
Arrays.asList(BUCKET_GIF, BUCKET_BITMAP, BUCKET_BITMAP_DRAWABLE));
}
this.modelLoaderRegistry 是一个重要的成员变量,它里面有 ModelLoaderFactory ,而ModelLoaderFactory 它是用来创建**ModelLoader **的。这个ModelLoader 很重要,所以大家要对他有一定的印象。那么ModelLoader是做什么的呢?
ModelLoader
作用:**一个工厂接口,用于将任意复杂的数据模型转换为可由 DataFetcher 用于获取模型所代表的资源数据的具体数据类型。**可以翻译为加载器。
该接口有两个目标: 1. 将特定模型转换为可解码为资源的数据类型。 2. 允许模型与视图的维度相结合以获取特定大小的资源。这样是为了节省空间。就是可以根据ImageView 的大小加载图片
Glide 初始化时会注册很多个 ModelLoader ,除了在创建Glide时 通过registry 注册的,还会注册那些用户在 manifest 中配置的 ModelLoader 。 那么我们来看看modelLoader 里面有什么功能
@Nullable
LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
@NonNull Options options);
boolean handles(@NonNull Model model);
LoadData 是ModelLoader 的内部类 ,主要作用就是装了三个东西:
- 用于识别资源唯一性的 Key;
- 缓存相关的备用 Key 列表
DataFetcher
其中 DataFetcher 最重要,加载资源的根源就在这里,例如发起网络请求等等,都在这个里面。
来看回Register的构造函数里面还有一些参数
this.decoderRegistry = new ResourceDecoderRegistry();
this.resourceEncoderRegistry = new ResourceEncoderRegistry();
decoderRegistry 保存的是ResourceDecoder 对象
ResourceDecoder与ResourceEecoder
ResourceDecoder 的作用是将ModelLoader 加载出来的数据,进行解码,解码成Bitmap ,或者BitmapDrawable 之类的。Glide中常用的Decoder有两个,其他都是将这两个Decoder进行包装,它们分别是ByteBufferBitmapDecoder 和StreamBitmapDecoder 。ResourceEncoder 的作用正好相反。
DataRewinder
this.dataRewinderRegistry = new DataRewinderRegistry();
dataRewinderRegistry 中保存的是DataRewinder
Rewinder 担任的是ModelLoader 到ResourceDecoder 的桥梁的角色,DecodeJob 将ModelLoader 获得的数据,构造出DataRewinder ,然后使用Rewinder将数据传给ResourceDecoder 进行解码。
Encoder
this.encoderRegistry = new EncoderRegistry();
encoderRegistry 中保存的是 Encoder 对象。Encoder 的作用是将数据转换成文件,用来配合Glide硬盘缓存。所以Encoder 的相关类,都是转为File类型的。
好,这个Registry 的知识点,大家要多看一遍,不并且理清楚他们之间的关系,不然后面讲解会一脸懵逼,不知道哪个类是做什么的。
到这里Register的创建也讲解完了,也就是Glide的创建也讲解完了。那么现在我们应该回到哪一步?很多人看到这已经蒙圈了,接下来的下一步做什么自己不知道了,因为我们看源码的时候很容易跟着跟着就忘记自己一开始是为了看哪个方法而进去的了,这时候会一直回退啊回退。但是如果有博客给你参考一下有不一样了,因为博客整体下来是一个链式的形式,我们可以更加清晰的知道我们下一步做啥,废话说多了。
我们回到Glide.with(context) 方法里面
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
这里面我们是不是只看了getRetriever(context) 里面的代码逻辑,也就是说,还有一部分没有看,既然我们要看Glide.with(context) 我们就看完它。
RequestManagerRetriever#get()
@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);
}
从这里可以知道get()有很多重载函数,看一下这里的逻辑,这里判断了如果当前线程是后台线程,那么就直接调用getApplicationManager(context) ,然后返回一个RequestManager
RequestManagerRetriever#getApplicationManager(后台线程)
@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;
}
如果是前台线程会做什么,他们最终会调用RequestManagerRetriever 的get 重载函数
public RequestManager get(@NonNull View view)
public RequestManager get(@NonNull Activity activity)
public RequestManager get(@NonNull Fragment fragment)
public RequestManager get(@NonNull FragmentActivity activity)
get(View) 和get(Context) 会根据情况调用get(Fragment) 或get(FragmentActivity) 。其中get(View) 为了找到一个合适的Fragment 或fallback Activity ,内部操作比较多,开销比较大,不要轻易使用。
RequestManagerRetriever#supportFragmentGet
get(Fragment) 和get(FragmentActivity) 方法都会调用supportFragmentGet 方法,只是传入参数不同:
FragmentManager fm = activity.getSupportFragmentManager();
supportFragmentGet(activity, fm, null, isActivityVisible(activity));
FragmentManager fm = fragment.getChildFragmentManager();
supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
注意:Glide会使用一个加载目标所在的宿主Activity或Fragment的子Fragment 来安全保存一个RequestManager ,而RequestManager 被Glide用来开始、停止、管理Glide请求。
而supportFragmentGet 就是创建/获取这个SupportRequestManagerFragment ,并返回其持有的RequestManager 的方法。
@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) {
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;
}
到这里RequestManager 就创建完成了。现在还有一个get(Activity)的重载函数没有分析。这个函数最终调用逻辑和get(Fragment)差不多,只不过他在中间创建的Fragment 是RequestManagerFragment 。其余的逻辑都是一样的了。所以整一个Glide.with的过程也就分析完成了。其实他里面主要做的一些事情就是初始化一些配置,并且创建一些类,比如Glide的创建,Register的创建,RequestManager 的创建。接下来我们用一张图来总结Glide.with的流程:如下
好了,到这里第一部分的内容已经讲解完了,在讲解第二部分的内容的时候。我们先思考一个问题,如果要显示一张图片需要几个步骤?我个人认为需要以下步骤
这其中获取资源是不是还可以分本地资源,网络资源,是不是从缓存获取等等?那么回过头来看一下Glide.with 做了什么?以上步骤,他一步也没有做。它仅仅是做了一些初始化的工作而已,所以我们离分析完Gilde 源码还有一大段路。不是我打击大家的学习信心,因为Glide的源码确实比较多。那么接下来就开始第二部分的讲解吧。
RequestManager.load
RequestManager的load方法也有很多重载函数,他们都调用了asDrawable()方法,然后再调用对应的load方法
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
RequestManager#asDrawable
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
RequestBuilder#RequestBuilder()
在RequestBuilder 的构造器方法方法中将Drawable.class 这样的入参保存到了transcodeClass 变量中:
@SuppressLint("CheckResult")
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
到这里,asDrawable() 的流程就分析完毕了,返回的是一个RequestBuilder ,然后再调用RequestBuilder 的load方法
RequestBuilder#load
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
我们发现这里也没有做什么其他操作,只是将isModelSe 设置为true而已。所以第二部分分析也完了,这里就两个重要的事情,一个是创建看RequestBuilder
一个是将RequestBuilder 中的isModelSet 设置为true。由于这个第二部分比较简单,所以在这里,就不画流程图了,大家应该也能看得懂。Glide源码讲到这里,还是没有讲解到怎么获取资源。所以接下来第三部分RequestBuilder.into 也就是Glide最核心的部分了。这一部分的内容比前面两节多很多,希望大家耐心看完。因为它里面做了三件事,也就是我们上面提到的三件事。
所以into如果要分步骤看的话,也可以按照三面的三大步骤来看。
RequestBuilder.into
首先看into(ImageView) 函数
public ViewTarget<ImageView, TranscodeType> 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:
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
null,
requestOptions,
Executors.mainThreadExecutor());
}
到这里很多人又开始疑惑了,我到底是去看into这个重载函数,还是先看glideContext.buildImageViewTarget ,这种情况的先,哪个先执行看哪个。很明显这里是glideContext.buildImageViewTarget 先执行,所以我们优先看一下这里面做了什么。
GlideContext#buildImageViewTarget
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
Glide内部只维护了两种target,一种是BitmapImageViewTarget ,另一种则是DrawableImageViewTarget ,由于我们前面使用的是load(String),这里最终返回的是DrawableImageViewTarget 。这里需要稍微留意一下,因为最终显示资源的那一步决定我们去DrawableImageViewTarget 中看还是BitmapImageViewTarget 中看。
RequestBuilder#into
接下来我们重新回到into的重载函数里面
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> 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;
}
到这里很多人开始问了,这里这么多代码逻辑,我们应该怎么去看源码?首先还是之前的原则,哪一个开始就先看哪一个。先看怎么创建Request,以及这个Request的类型是什么,然后我们才能去看下一步是做什么。
RequestBuilder#buildRequest
下面这段代码有点长,因为我把整一个创建Request的调用链放在了一段代码里面,是为了大家更加方便的阅读,至于每一段代码做了什么,我会在通过注释的方式写明。
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
new Object(),
target,
targetListener,
null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request buildRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
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 buildThumbnailRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
if (thumbnailBuilder != null) {
} else if (thumbSizeMultiplier != null) {
} else {
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> 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);
}
现在SingleRequest 创建完了,那么下一步应该看哪里?下一步是不是回到into方法里面?因为我们into方法还没看完,只是看了into中的buildRequest 方法而已,然而他里面还有其他的方法没有看。创建了Request之后,就要执行Request,而执行Request的代码是在requestManager.track(target, request); 中的。接下来我们要看的就是requestManager.track(target, request);
RequestManager#track
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
在这里面,targetTracker 成员变量在声明的时候直接初始化为TargetTracker 类的无参数实例,该类的作用是保存所有的Target并向它们转发生命周期事件;requestTracker 在RequestManager 的构造器中传入了new RequestTracker() ,该类的作用管理所有状态的请求。
RequestTracker#runRequest
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 吗?返回的Request 的类型其实是SingleRequest 。所以这里我们要看SingleRequest 的begin()方法了
SingleRequest#begin
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));
}
}
}
SingleRequest#onSizeReady
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));
}
}
}
最终这里调用的是engine的load方法去进行网络请求
Engine#load
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,
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);
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
Engine#waitForExistingOrStartNewJob
private <R> LoadStatus waitForExistingOrStartNewJob(
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,
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<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> 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);
}
到这里我们应该往哪一部分走呢,其实很明显了,engineJob.start 这的命名已经告诉我们要开始了。所以我们点击进去看一下这方法。
EngineJob#start
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
DecodeJob#willDecodeFromCache
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
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:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
然后执行 executor.execute(decodeJob); 这段代码最终执行的是decodeJob 里面的run方法,所以接下来我们要分析的是decodeJob 的run方法。run方法没什么好讲的,他里面就是调用了runWrapped 方法,所以直接看runWrapped 方法就好。到这里,我们还没有开始有网络请求,我们目前在做的工作都还只是网络请求前的一些准备工作。好了接着继续看,下面这一段,我任务是是整一个Glide加载流程最难读懂的地方了,这里希望大家认真仔细阅读接下来的这段代码。
DecodeJob#runWrapped
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:
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);
}
}
综合以上代码,可以知道,getNextStage() 返回的状态是RESOURCE_CACHE ,所以getNextGenerator() 得到的是ResourceCacheGenerator ,然后去执行了runGenerators() 方法
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();
}
}
在之前,我们分析过currentGenerator 的类型是ResourceCacheGenerator ,所以接下来的代码就是ResourceCacheGenerator 里面的startNext() 方法了
ResourceCacheGenerator#startNext
整体来说这里面的代码比较复杂,而且调用的层次会很深,所以我们这里先跟着代码一步一步走,然后最后整体来看,这段代码做了什么。
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
if (sourceIds.isEmpty()) {
return false;
}
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
if (resourceClasses.isEmpty()) {
if (File.class.equals(helper.getTranscodeClass())) {
return false;
}
throw new IllegalStateException(
"Failed to find any load path from "
+ helper.getModelClass()
+ " to "
+ helper.getTranscodeClass());
}
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
currentKey =
new ResourceCacheKey(
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
所以说这里第一次ResourceCacheGenerator 的startNext() 返回的是false,回到上一步也就是DecodeJob#runGenerators 中,那么就进入的while循环,然后他又通过getNextGenerator 去获取了其他的Generator ,这一次获取到的是DataCacheGenerator ,所以接下来要调用的就是DataCacheGenerator#startNext 方法了
DataCacheGenerator#startNext
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
这里的代码其实就是去缓存里面去寻找文件,如果能找到就直接返回了,那么第一次的话,肯定是找不到的,所以我们直接看最后的SourceGenerator 的startNext 方法吧
SourceGenerator#startNext
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;
}
到这里,发现在这里呢还是没有进行一个数据的请求,数据的请求也就是在loadData.fetcher.loadData(); 这中去进行的,要了解这段里面的代码,我们首先要知道loadData 的类型和fetcher 的类型,这里分析起来就比较难了,因为它是在ResourceCacheGenerator#startNext 里面去做的。这里我们就不详细分析了。因为要弄清楚这些是怎么来的,还得去Glide的构建函数说起,这样就太不利于我们一个整体流程分析的分析了。这里的loadData 是MultiModelLoader 里面得到的,而他里面有loadData ,loadData 里面的fetcher 类型其实是HttpUrlFetcher 。所以接下来我们要看的地方是HttpUrlFetcher 里面的loadData 方法。
HttpUrlFetcher #loadData
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> 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<String, String> 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) {
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> 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.fetcher.loadData(helper.getPriority(), this); 可以看到传入this,this就是callback.onDataReady(result); 语句中的callback, 是 SourceGenerator 的类型。其实就是接口 DataCallback ,接下来就看看该接口在SourceGenerator 中的实现。看到这里,我们可以说是完成了整一个Glide的请求数据部分了,接下来就是要弄明白,请求回来的数据经过怎样的处理,然后显示到imageView 上。现在开始,需要我们一步步’回去‘了,什么意思?就是我们看整一个流程,是一个来,一个回的流程,获取数据这一部分,是一步一步深入的过程,那么得到数据之后,那么肯定就需要我们一步步回去了,看其他框架的源码也是这么一个过程。要记住一点,我们深入的目的是什么,达到了目的之后,需要怎么做。正如那一句话讲的:不要忘记我们出发的目的。
而且思考一个问题,我们获取到对应的数据之后,就能直接显示图片了吗?肯定是不可以的,肯定还是要经过一些列的处理之后,才能将数据转换成我们需要的数据,并经行最终的显示。所以接下来我们要做的就是看Glide是怎么将数据转换成我们需要的数据源,并显示的。
SourceGenerator#onDataReady
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
我们解读一下onDataReady 里面的代码。首先,获取DiskCacheStrategy 判断能不能被缓存,这里的判断代码在SourceGenerator.startNext() 中出现过,显然是可以的。然后将data保存到dataToCache ,并调用cb.reschedule() 。 我们在前面分析过,该方法的作用就是将DecodeJob 提交到Glide的source线程池中。然后执行DecodeJob.run() 方法,经过runWrapped() 、 runGenerators() 方法后,又回到了SourceGenerator.startNext() 方法。
在方法的开头,会判断dataToCache 是否为空,此时显然不为空,所以会调用cacheData(Object) 方法进行data的缓存处理。缓存完毕后,会为该缓存文件生成一个SourceCacheGenerator 。然后在startNext() 方法中会直接调用该变量进行加载。
SourceGenerator#startNext
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
}
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
由于在构造DataCacheGenerator 时,指定了FetcherReadyCallback 为自己,所以DataCacheGenerator 加载结果会由SourceGenerator 转发给DecodeJob 。这里的回调过程是怎么样的呢?如下,
DataCacheGenerator#startNext
->HttpUrlFetcher #loadData
->DataCacheGenerator#onDataReady
->SourceGenerator#onDataFetcherReady
这里经过一些回调之后,最终会回调到SourceGenerator 的onDataFetcherReady ,而这里面调用的是DecodeJob 的onDataFetcherReady 方法
所以接下来看一下这个方法
DecodeJob#onDataFetcherReady
@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() {
Resource<R> 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 <Data> Resource<R> decodeFromData(
DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
以上这些代码流程放在一起,因为这里面只是保证了一个流程的完整性,代码没什么好讲的,都是一步步的调用。但是有一个地方要注意以下,那就是DataRewinder ,这是一个将数据流里面的指针重新指向开头的类,在调用ResourceDecoder 对data进行编码时会尝试很多个编码器,所以每一次尝试后都需要重置索引。在Glide初始化的时候默认注入了ByteBufferRewinder 和InputStreamRewinder 这两个类的工厂。这样就为ByteBuffer 和InputStream 的重定向提供了实现。还有就是DecodeCallback ,这里面其实回调最终还是要回调到DecodeJob 对应的代码里面。
LoadPath#load
public Resource<Transcode> load(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback)
throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
DecodePath#decode
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
这里面只要是三步:
- 使用
ResourceDecoderList 进行decode - 将
decoded 的资源进行transform - 将transformed的资源进行
transcode
DecodePath#decodeResource
@NonNull
private Resource<ResourceType> decodeResource(
DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
throws GlideException {
List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
private Resource<ResourceType> decodeResourceWithList(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
List<Throwable> exceptions)
throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
} 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;
}
ByteBufferBitmapDecoder#decode()
@Override
public Resource<Bitmap> decode(
@NonNull ByteBuffer source, int width, int height, @NonNull Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
接下来要执行的就是 callback.onResourceDecoded(decoded); 这里调用的是DecodeJob里面的方法
DecodeJob#onResourceDecoded
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey, dataSource, encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
这一段代码执行完成之后,就回到了回到上面三步的第三步了transformed 了,这里的这里的transcoder 就是BitmapDrawableTranscoder ,该方法返回了一个LazyBitmapDrawableResource 。然后接下来怎么走?到这里很多人又迷糊了,现在我们得到了我们想要的资源了,那接下来肯定就是要将这个资源显示出来了。哪一些代码会是显示呢?其实上面我们已经提到过了,在decodeFromRetrievedData 中,还有一个方法没有分析,notifyEncodeAndRelease 这个方法就是去完成显示的功能的。
DecodeJob#notifyEncodeAndRelease
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> 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();
}
}
onEncodeComplete();
}
EngineJob#notifyCallbacksOfResult
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);
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();
}
这里,我们需要注意的是CallResourceReady ,这是EngineJob 的一个内部类,看到这里,我们应该知道这是要就是要去CallResourceReady 里面的run方法了
CallResourceReady#run
public void run() {
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
EngineJob#callCallbackOnResourceReady
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
cb.onResourceReady(engineResource, dataSource);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
SingleRequest#onResourceReady
注意,这里我们看的是它的重载方法
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
ImageViewTarget # onResourceReady
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
setResource(resource);
maybeUpdateAnimatable(resource);
}
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
到这里整一个Glide的加载流程就全部讲解完毕了,这个流程一看下来确实很长,也花费了我不少时间和心思。毕竟这一篇博客实在是太长了,很少有人能耐心看下去。但是看下去的人肯定对Glide的加载流程有一个很深的映像了。分析整一个流程的时候,我们是忽略了一些细节的,比如说缓存那一块,比如说bitmap优化处理那一块。因为这一些对整一个流程影响不大,所以,这篇博客就先暂时忽略了。Glide的缓存是很重要的一个知识点,所以之后可能还考虑单独出一篇关于Glide缓存的博客。
到这里正文的内容基本也就结束了,但是还是请读者思考几个问题
- 整一个Glide的流程大体是怎么样的?
- 如果给你设计一个图片加载库,哪一些流程是必备的,又有哪一些地方是可以优化的?
- 通过阅读这一篇博客,你收获了什么?
|