知识为进步之母,而进步又为富强之源泉。这篇文章主要讲述Android图片加载框架最全解析,从源码的角度理解Glide的执行流程相关的知识,希望能为你提供帮助。
转载请注明出处:
http://blog.csdn.net/guolin_blog/article/details/53939176
本文同步发表于我的微信公众号, 扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注, 每天都有文章更新。在本系列的上一篇文章中, 我们学习了Glide的基本用法, 体验了这个图片加载框架的强大功能, 以及它非常简便的API。还没有看过上一篇文章的朋友, 建议先去阅读 Android图片加载框架最全解析( 一) , Glide的基本用法 。
在多数情况下, 我们想要在界面上加载并展示一张图片只需要一行代码就能实现, 如下所示:
Glide.with(this).load(url).into(imageView);
虽说只有这简简单单的一行代码, 但大家可能不知道的是, Glide在背后帮我们默默执行了成吨的工作。这个形容词我想了很久, 因为我觉得用非常多这个形容词不足以描述Glide背后的工作量, 我查到的英文资料是用tons of work来进行形容的, 因此我觉得这里使用成吨来形容更加贴切一些。
虽说我们在平时使用Glide的时候格外地简单和方便, 但是知其然也要知其所以然。那么今天我们就来解析一下Glide的源码, 看看它在这些简单用法的背后, 到底执行了多么复杂的工作。
如何阅读源码 在开始解析Glide源码之前, 我想先和大家谈一下该如何阅读源码, 这个问题也是我平时被问得比较多的, 因为很多人都觉得阅读源码是一件比较困难的事情。
那么阅读源码到底困难吗? 这个当然主要还是要视具体的源码而定。比如同样是图片加载框架, 我读Volley的源码时就感觉酣畅淋漓, 并且对Volley的架构设计和代码质量深感佩服。读Glide的源码时却让我相当痛苦, 代码极其难懂。当然这里我并不是说Glide的代码写得不好, 只是因为Glide和复杂程度和Volley完全不是在一个量级上的。
那么, 虽然源码的复杂程度是外在的不可变条件, 但我们却可以通过一些技巧来提升自己阅读源码的能力。这里我和大家分享一下我平时阅读源码时所使用的技巧, 简单概括就是八个字: 抽丝剥茧、点到即止。应该认准一个功能点, 然后去分析这个功能点是如何实现的。但只要去追寻主体的实现逻辑即可, 千万不要试图去搞懂每一行代码都是什么意思, 那样很容易会陷入到思维黑洞当中, 而且越陷越深。因为这些庞大的系统都不是由一个人写出来的, 每一行代码都想搞明白, 就会感觉自己是在盲人摸象, 永远也研究不透。如果只是去分析主体的实现逻辑, 那么就有比较明确的目的性, 这样阅读源码会更加轻松, 也更加有成效。
而今天带大家阅读的Glide源码就非常适合使用这个技巧, 因为Glide的源码太复杂了, 千万不要试图去搞明白它每行代码的作用, 而是应该只分析它的主体实现逻辑。那么我们本篇文章就先确立好一个目标, 就是要通过阅读源码搞明白下面这行代码:
Glide.with(this).load(url).into(imageView);
到底是如何实现将一张网络图片展示到ImageView上面的。先将Glide的一整套图片加载机制的基本流程梳理清楚, 然后我们再通过后面的几篇文章具体去了解Glide源码方方面面的细节。
准备好了吗? 那么我们现在开始。
源码下载 既然是要阅读Glide的源码, 那么我们自然需要先将Glide的源码下载下来。其实如果你是使用在build.gradle中添加依赖的方式将Glide引入到项目中的, 那么源码自动就已经下载下来了, 在android Studio中就可以直接进行查看。
不过, 使用添加依赖的方式引入的Glide, 我们只能看到它的源码, 但不能做任何的修改, 如果你还需要修改它的源码的话, 可以到GitHub上面将它的完整源码下载下来。
Glide的GitHub主页的地址是: https://github.com/bumptech/glide
不过在这个地址下载到的永远都是最新的源码, 有可能还正在处于开发当中。而我们整个系列都是使用Glide 3.7.0这个版本来进行讲解的, 因此如果你需要专门去下载3.7.0版本的源码, 可以到这个地址进行下载: https://github.com/bumptech/glide/tree/v3.7.0
开始阅读 我们在上一篇文章中已经学习过了, Glide最基本的用法就是三步走: 先with(), 再load(), 最后into()。那么我们开始一步步阅读这三步走的源码, 先从with()看起。
1. with() with()方法是Glide类中的一组静态方法, 它有好几个方法重载, 我们来看一下Glide类中所有with()方法的方法重载:
public class Glide {...public static RequestManager with(Context context) {
RequestManagerRetriever retriever =
RequestManagerRetriever.get();
return retriever.get(context);
}public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever =
RequestManagerRetriever.get();
return retriever.get(activity);
}public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever =
RequestManagerRetriever.get();
return retriever.get(activity);
}@
TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever =
RequestManagerRetriever.get();
return retriever.get(fragment);
}public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever =
RequestManagerRetriever.get();
return retriever.get(fragment);
}
}
可以看到, with()方法的重载种类非常多, 既可以传入Activity, 也可以传入Fragment或者是Context。每一个with()方法重载的代码都非常简单, 都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象, 这个静态get()方法就是一个单例实现, 没什么需要解释的。然后再调用RequestManagerRetriever的实例get()方法, 去获取RequestManager对象。
而RequestManagerRetriever的实例get()方法中的逻辑是什么样的呢? 我们一起来看一看:
public class RequestManagerRetriever implements Handler.Callback {private static final RequestManagerRetriever INSTANCE =
new RequestManagerRetriever();
private volatile RequestManager applicationManager;
.../**
* Retrieves and returns the RequestManagerRetriever singleton.
*/
public static RequestManagerRetriever get() {
return INSTANCE;
}private RequestManager getApplicationManager(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.
applicationManager =
new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}public RequestManager get(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) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm =
activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}public RequestManager get(Fragment fragment) {
if (fragment.getActivity() =
=
null) {
throw new IllegalArgumentException("
You cannot start a load on a fragment before it is attached"
);
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm =
fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}@
TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT <
Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm =
activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}@
TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >
=
Build.VERSION_CODES.JELLY_BEAN_MR1 &
&
activity.isDestroyed()) {
throw new IllegalArgumentException("
You cannot start a load for a destroyed activity"
);
}
}@
TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public RequestManager get(android.app.Fragment fragment) {
if (fragment.getActivity() =
=
null) {
throw new IllegalArgumentException("
You cannot start a load on a fragment before it is attached"
);
}
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT <
Build.VERSION_CODES.JELLY_BEAN_MR1) {
return get(fragment.getActivity().getApplicationContext());
} else {
android.app.FragmentManager fm =
fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm);
}
}@
TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current =
(RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current =
=
null) {
current =
pendingRequestManagerFragments.get(fm);
if (current =
=
null) {
current =
new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}@
TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current =
getRequestManagerFragment(fm);
RequestManager requestManager =
current.getRequestManager();
if (requestManager =
=
null) {
requestManager =
new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current =
=
null) {
current =
pendingSupportRequestManagerFragments.get(fm);
if (current =
=
null) {
current =
new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm);
RequestManager requestManager =
current.getRequestManager();
if (requestManager =
=
null) {
requestManager =
new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}...
}
上述代码虽然看上去逻辑有点复杂, 但是将它们梳理清楚后还是很简单的。RequestManagerRetriever类中看似有很多个get()方法的重载, 什么Context参数, Activity参数, Fragment参数等等, 实际上只有两种情况而已, 即传入Application类型的参数, 和传入非Application类型的参数。
我们先来看传入Application参数的情况。如果在Glide.with()方法中传入的是一个Application对象, 那么这里就会调用带有Context参数的get()方法重载, 然后会在第44行调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况, 因为Application对象的生命周期即应用程序的生命周期, 因此Glide并不需要做什么特殊的处理, 它自动就是和应用程序的生命周期是同步的, 如果应用程序关闭的话, Glide的加载也会同时终止。
接下来我们看传入非Application参数的情况。不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment, 最终的流程都是一样的, 那就是会向当前的Activity当中添加一个隐藏的Fragment。具体添加的逻辑是在上述代码的第117行和第141行, 分别对应的app包和v4包下的两种Fragment的情况。那么这里为什么要添加一个隐藏的Fragment呢? 因为Glide需要知道加载的生命周期。很简单的一个道理, 如果你在某个Activity上正在加载着一张图片, 结果图片还没加载出来, Activity就被用户关掉了, 那么图片还应该继续加载吗? 当然不应该。可是Glide并没有办法知道Activity的生命周期, 于是Glide就使用了添加隐藏Fragment的这种小技巧, 因为Fragment的生命周期和Activity是同步的, 如果Activity被销毁了, Fragment是可以监听到的, 这样Glide就可以捕获这个事件并停止图片加载了。
这里额外再提一句, 从第48行代码可以看出, 如果我们是在非主线程当中使用的Glide, 那么不管你是传入的Activity还是Fragment, 都会被强制当成Application来处理。不过其实这就属于是在分析代码的细节了, 本篇文章我们将会把目光主要放在Glide的主线工作流程上面, 后面不会过多去分析这些细节方面的内容。
总体来说, 第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已, 然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期, 并没有什么特别复杂的逻辑。不过复杂的逻辑还在后面等着我们呢, 接下来我们开始分析第二步, load()方法。
2. load() 由于with()方法返回的是一个RequestManager对象, 那么很容易就能想到, load()方法是在RequestManager类当中的, 所以说我们首先要看的就是RequestManager这个类。不过在上一篇文章中我们学过, Glide是支持图片URL字符串、图片本地路径等等加载形式的, 因此RequestManager中也有很多个load()方法的重载。但是这里我们不可能把每个load()方法的重载都看一遍, 因此我们就只选其中一个加载图片URL字符串的load()方法来进行研究吧。
【Android图片加载框架最全解析,从源码的角度理解Glide的执行流程】RequestManager类的简化代码如下所示:
public class RequestManager implements LifecycleListener {.../**
* Returns a request builder to load the given {@
link String}.
* signature.
*
* @
see #fromString()
* @
see #load(Object)
*
* @
param string A file path, or a uri or url handled by {@
link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<
String>
load(String string) {
return (DrawableTypeRequest<
String>
) fromString().load(string);
}/**
* Returns a request builder that loads data from {@
link String}s using an empty signature.
*
* <
p>
*Note - this method caches data using only the given String as the cache key. If the data is a Uri outside of
*your control, or you otherwise expect the data represented by the given String to change without the String
*identifier changing, Consider using
*{@
link GenericRequestBuilder#signature(Key)} to mixin a signature
*you create that identifies the data currently at the given String that will invalidate the cache if that data
*changes. Alternatively, using {@
link DiskCacheStrategy#NONE} and/or
*{@
link DrawableRequestBuilder#skipMemoryCache(boolean)} may be appropriate.
* <
/p>
*
* @
see #from(Class)
* @
see #load(String)
*/
public DrawableTypeRequest<
String>
fromString() {
return loadGeneric(String.class);
}private <
T>
DrawableTypeRequest<
T>
loadGeneric(Class<
T>
modelClass) {
ModelLoader<
T, InputStream>
streamModelLoader =
Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<
T, ParcelFileDescriptor>
fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass !=
null &
&
streamModelLoader =
=
null &
&
fileDescriptorModelLoader =
=
null) {
throw new IllegalArgumentException("
Unknown type "
+
modelClass +
"
. You must provide a Model of a type for"
+
"
which there is a registered ModelLoader, if you are using a custom model, you must first call"
+
"
Glide#register with a ModelLoaderFactory for your custom model class"
);
}
return optionsApplier.apply(
new DrawableTypeRequest<
T>
(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}...}
RequestManager类的代码是非常多的, 但是经过我这样简化之后, 看上去就比较清爽了。在我们只探究加载图片URL字符串这一个load()方法的情况下, 那么比较重要的方法就只剩下上述代码中的这三个方法。
那么我们先来看load()方法, 这个方法中的逻辑是非常简单的, 只有一行代码, 就是先调用了fromString()方法, 再调用load()方法, 然后把传入的图片URL地址传进去。而fromString()方法也极为简单, 就是调用了loadGeneric()方法, 并且指定参数为String.class, 因为load()方法传入的是一个字符串参数。那么看上去, 好像主要的工作都是在loadGeneric()方法中进行的了。
其实loadGeneric()方法也没几行代码, 这里分别调用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象。ModelLoader对象是用于加载图片的, 而我们给load()方法传入不同类型的参数, 这里也会得到不同的ModelLoader对象。不过buildStreamModelLoader()方法内部的逻辑还是蛮复杂的, 这里就不展开介绍了, 要不然篇幅实在收不住, 感兴趣的话你可以自己研究。由于我们刚才传入的参数是String.class, 因此最终得到的是StreamStringLoader对象, 它是实现了ModelLoader接口的。
最后我们可以看到, loadGeneric()方法是要返回一个DrawableTypeRequest对象的, 因此在loadGeneric()方法的最后又去new了一个DrawableTypeRequest对象, 然后把刚才获得的ModelLoader对象, 还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就不解释了, 我们只看主线流程。
那么这个DrawableTypeRequest的作用是什么呢? 我们来看下它的源码, 如下所示:
public class DrawableTypeRequest<
ModelType>
extends DrawableRequestBuilder<
ModelType>
implements DownloadOptions {
private final ModelLoader<
ModelType, InputStream>
streamModelLoader;
private final ModelLoader<
ModelType, ParcelFileDescriptor>
fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static <
A, Z, R>
FixedLoadProvider<
A, ImageVideoWrapper, Z, R>
buildProvider(Glide glide,
ModelLoader<
A, InputStream>
streamModelLoader,
ModelLoader<
A, ParcelFileDescriptor>
fileDescriptorModelLoader, Class<
Z>
resourceClass,
Class<
R>
transcodedClass,
ResourceTranscoder<
Z, R>
transcoder) {
if (streamModelLoader =
=
null &
&
fileDescriptorModelLoader =
=
null) {
return null;
}if (transcoder =
=
null) {
transcoder =
glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<
ImageVideoWrapper, Z>
dataLoadProvider =
glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<
A>
modelLoader =
new ImageVideoModelLoader<
A>
(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<
A, ImageVideoWrapper, Z, R>
(modelLoader, transcoder, dataLoadProvider);
}DrawableTypeRequest(Class<
ModelType>
modelClass, ModelLoader<
ModelType, InputStream>
streamModelLoader,
ModelLoader<
ModelType, ParcelFileDescriptor>
fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader =
streamModelLoader;
this.fileDescriptorModelLoader =
fileDescriptorModelLoader;
this.optionsApplier =
optionsApplier;
}/**
* Attempts to always load the resource as a {@
link android.graphics.Bitmap}, even if it could actually be animated.
*
* @
return A new request builder for loading a {@
link android.graphics.Bitmap}
*/
public BitmapTypeRequest<
ModelType>
asBitmap() {
return optionsApplier.apply(new BitmapTypeRequest<
ModelType>
(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}/**
* Attempts to always load the resource as a {@
link com.bumptech.glide.load.resource.gif.GifDrawable}.
* <
p>
*If the underlying data is not a GIF, this will fail. As a result, this should only be used if the model
*represents an animated GIF and the caller wants to interact with the GIfDrawable directly. Normally using
*just an {@
link DrawableTypeRequest} is sufficient because it will determine whether or
*not the given data represents an animated GIF and return the appropriate animated or not animated
*{@
link android.graphics.drawable.Drawable} automatically.
* <
/p>
*
* @
return A new request builder for loading a {@
link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest<
ModelType>
asGif() {
return optionsApplier.apply(new GifTypeRequest<
ModelType>
(this, streamModelLoader, optionsApplier));
}...
}
这个类中的代码本身就不多, 我只是稍微做了一点简化。可以看到, 最主要的就是它提供了asBitmap()和asGif()这两个方法。这两个方法我们在上一篇文章当中都是学过的, 分别是用于强制指定加载静态图片和动态图片。而从源码中可以看出, 它们分别又创建了一个BitmapTypeRequest和GifTypeRequest, 如果没有进行强制指定的话, 那默认就是使用DrawableTypeRequest。
好的, 那么我们再回到RequestManager的load()方法中。刚才已经分析过了, fromString()方法会返回一个DrawableTypeRequest对象, 接下来会调用这个对象的load()方法, 把图片的URL地址传进去。但是我们刚才看到了, DrawableTypeRequest中并没有load()方法, 那么很容易就能猜想到, load()方法是在父类当中的。
DrawableTypeRequest的父类是DrawableRequestBuilder, 我们来看下这个类的源码:
public class DrawableRequestBuilder<
ModelType>
extends GenericRequestBuilder<
ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
implements BitmapOptions, DrawableOptions {DrawableRequestBuilder(Context context, Class<
ModelType>
modelClass,
LoadProvider<
ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>
loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}public DrawableRequestBuilder<
ModelType>
thumbnail(
DrawableRequestBuilder<
?>
thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
thumbnail(
GenericRequestBuilder<
?, ?, ?, GlideDrawable>
thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
decoder(ResourceDecoder<
ImageVideoWrapper, GifBitmapWrapper>
decoder) {
super.decoder(decoder);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
cacheDecoder(ResourceDecoder<
File, GifBitmapWrapper>
cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
encoder(ResourceEncoder<
GifBitmapWrapper>
encoder) {
super.encoder(encoder);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
priority(Priority priority) {
super.priority(priority);
return this;
}public DrawableRequestBuilder<
ModelType>
transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}public DrawableRequestBuilder<
ModelType>
centerCrop() {
return transform(glide.getDrawableCenterCrop());
}public DrawableRequestBuilder<
ModelType>
fitCenter() {
return transform(glide.getDrawableFitCenter());
}public DrawableRequestBuilder<
ModelType>
bitmapTransform(Transformation<
Bitmap>
... bitmapTransformations) {
GifBitmapWrapperTransformation[] transformations =
new GifBitmapWrapperTransformation[bitmapTransformations.length];
for (int i =
0;
i <
bitmapTransformations.length;
i+
+
) {
transformations[i] =
new GifBitmapWrapperTransformation(glide.getBitmapPool(), bitmapTransformations[i]);
}
return transform(transformations);
}@
Override
public DrawableRequestBuilder<
ModelType>
transform(Transformation<
GifBitmapWrapper>
... transformation) {
super.transform(transformation);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
transcoder(
ResourceTranscoder<
GifBitmapWrapper, GlideDrawable>
transcoder) {
super.transcoder(transcoder);
return this;
}public final DrawableRequestBuilder<
ModelType>
crossFade() {
super.animate(new DrawableCrossFadeFactory<
GlideDrawable>
());
return this;
}public DrawableRequestBuilder<
ModelType>
crossFade(int duration) {
super.animate(new DrawableCrossFadeFactory<
GlideDrawable>
(duration));
return this;
}public DrawableRequestBuilder<
ModelType>
crossFade(int animationId, int duration) {
super.animate(new DrawableCrossFadeFactory<
GlideDrawable>
(context, animationId,
duration));
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
dontAnimate() {
super.dontAnimate();
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
animate(int animationId) {
super.animate(animationId);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
placeholder(Drawable drawable) {
super.placeholder(drawable);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
fallback(Drawable drawable) {
super.fallback(drawable);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
fallback(int resourceId) {
super.fallback(resourceId);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
error(int resourceId) {
super.error(resourceId);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
error(Drawable drawable) {
super.error(drawable);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
listener(
RequestListener<
? super ModelType, GlideDrawable>
requestListener) {
super.listener(requestListener);
return this;
}
@
Override
public DrawableRequestBuilder<
ModelType>
diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
override(int width, int height) {
super.override(width, height);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
sourceEncoder(Encoder<
ImageVideoWrapper>
sourceEncoder) {
super.sourceEncoder(sourceEncoder);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
dontTransform() {
super.dontTransform();
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
signature(Key signature) {
super.signature(signature);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
load(ModelType model) {
super.load(model);
return this;
}@
Override
public DrawableRequestBuilder<
ModelType>
clone() {
return (DrawableRequestBuilder<
ModelType>
) super.clone();
}@
Override
public Target<
GlideDrawable>
into(ImageView view) {
return super.into(view);
}@
Override
void applyFitCenter() {
fitCenter();
}@
Override
void applyCenterCrop() {
centerCrop();
}
}
DrawableRequestBuilder中有很多个方法, 这些方法其实就是Glide绝大多数的API了。里面有不少我们在上篇文章中已经用过了, 比如说placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等。当然还有很多暂时还没用到的API, 我们会在后面的文章当中学习。
到这里, 第二步load()方法也就分析结束了。为什么呢? 因为你会发现DrawableRequestBuilder类中有一个into()方法( 上述代码第220行) , 也就是说, 最终load()方法返回的其实就是一个DrawableTypeRequest对象。那么接下来我们就要进行第三步了, 分析into()方法中的逻辑。
3. into() 如果说前面两步都是在准备开胃小菜的话, 那么现在终于要进入主菜了, 因为into()方法也是整个Glide图片加载流程中逻辑最复杂的地方。
不过从刚才的代码来看, into()方法中并没有任何逻辑, 只有一句super.into(view)。那么很显然, into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。
DrawableRequestBuilder的父类是GenericRequestBuilder, 我们来看一下GenericRequestBuilder类中的into()方法, 如下所示:
public Target<
TranscodeType>
into(ImageView view) {
Util.assertMainThread();
if (view =
=
null) {
throw new IllegalArgumentException("
You must pass in a non null View"
);
}
if (!isTransformationSet &
&
view.getScaleType() !=
null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
这里前面一大堆的判断逻辑我们都可以先不用管, 等到后面文章讲transform的时候会再进行解释, 现在我们只需要关注最后一行代码。最后一行代码先是调用了glide.buildImageViewTarget()方法, 这个方法会构建出一个Target对象, Target对象则是用来最终展示图片用的, 如果我们跟进去的话会看到如下代码:
<
R>
Target<
R>
buildImageViewTarget(ImageView imageView, Class<
R>
transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
这里其实又是调用了ImageViewTargetFactory的buildTarget()方法, 我们继续跟进去, 代码如下所示:
public class ImageViewTargetFactory {@
SuppressWarnings("
unchecked"
)
public <
Z>
Target<
Z>
buildTarget(ImageView view, Class<
Z>
clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<
Z>
) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<
Z>
) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<
Z>
) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("
Unhandled class: "
+
clazz
+
"
, try .as*(Class).transcode(ResourceTranscoder)"
);
}
}
}
可以看到, 在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。那如果你要分析这个class参数是从哪儿传过来的, 这可有得你分析了, 简单起见我直接帮大家梳理清楚。这个class参数其实基本上只有两种情况, 如果你在使用Glide加载图片的时候调用了asBitmap()方法, 那么这里就会构建出BitmapImageViewTarget对象, 否则的话构建的都是GlideDrawableImageViewTarget对象。至于上述代码中的DrawableImageViewTarget对象, 这个通常都是用不到的, 我们可以暂时不用管它。
也就是说, 通过glide.buildImageViewTarget()方法, 我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行, 可以看到, 这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。我们来看一下这个into()方法的源码:
public <
Y extends Target<
TranscodeType>
>
Y into(Y target) {
Util.assertMainThread();
if (target =
=
null) {
throw new IllegalArgumentException("
You must pass in a non null Target"
);
}
if (!isModelSet) {
throw new IllegalArgumentException("
You must first set a model (try #load())"
);
}
Request previous =
target.getRequest();
if (previous !=
null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request =
buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
这里我们还是只抓核心代码, 其实只有两行是最关键的, 第15行调用buildRequest()方法构建出了一个Request对象, 还有第18行来执行这个Request。
Request是用来发出加载图片请求的, 它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
private Request buildRequest(Target<
TranscodeType>
target) {
if (priority =
=
null) {
priority =
Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}private Request buildRequestRecursive(Target<
TranscodeType>
target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder !=
null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("
You cannot use a request as both the main request and a thumbnail, "
+
"
consider using clone() on the request(s) passed to thumbnail()"
);
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory =
animationFactory;
}if (thumbnailRequestBuilder.priority =
=
null) {
thumbnailRequestBuilder.priority =
getThumbnailPriority();
}if (Util.isValidDimensions(overrideWidth, overrideHeight)
&
&
!Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest =
obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt =
true;
// Recursively generate thumbnail requests.
Request thumbRequest =
thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt =
false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier !=
null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest =
obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest =
obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}private Request obtainRequest(Target<
TranscodeType>
target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
可以看到, buildRequest()方法的内部其实又调用了buildRequestRecursive()方法, 而buildRequestRecursive()方法中的代码虽然有点长, 但是其中90%的代码都是在处理缩略图的。如果我们只追主线流程的话, 那么只需要看第47行代码就可以了。这里调用了obtainRequest()方法来获取一个Request对象, 而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数, 而其中很多的参数我们都是比较熟悉的, 像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此, 我们就有理由猜测, 刚才在load()方法中调用的所有API, 其实都是在这里组装到Request对象当中的。那么我们进入到这个GenericRequest的obtain()方法瞧一瞧:
public final class GenericRequest<
A, T, Z, R>
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 基于appium的移动端自动化测试,密码键盘无法识别问题
- 国内外Android有名开发人员的Blog和git
- 平台即服务| PaaS
- 如何在Python中使用OpenCV进行性别检测()
- 如何在Python中使用OpenCV进行年龄检测()
- 在Python中使用OpenCV进行年龄和性别检测
- 最新适用于Linux的7个最佳BitTorrent客户端
- 如何在基于Debian的Linux发行版中安装Zoom()
- 6款最佳Mac分区管理器合集详细介绍