本文大部分转自 郭霖的Glide 教程系列(本文总结)
https://mp.weixin.qq.com/s/p5mIA6nuVEWZsWPHOCHcYw
调用Glide.with()方法用于创建一个加载图片的实例。
with()方法可以接收Context、Activity或者Fragment类型的参数。
那如果调用的地方既不在Activity中也不在Fragment中呢?
也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。
注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。
如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止
Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。
// 加载本地图片File file = getImagePath();
Glide.with(this).load(file).into(imageView);
// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
基本用法
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading) //占位图
.error(R.drawable.error)//加载错误图片
.diskCacheStrategy(DiskCacheStrategy.NONE)//禁用掉缓存功能
.into(imageView);
加载gif 图片
.asGif() 指定加载gif
.asBitmap()指定加载静态图
Glide.with(this)
.load(url)
.asBitmap()
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
而使用Glide,完全不用担心图片内存浪费,甚至是内存溢出的问题。因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少
如果真需要 固定图片大小 .override(100, 100)
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)
.into(imageView);
Glide缓存
Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存。
内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。
缓存Key
Glide的缓存Key生成规则非常繁琐,决定缓存Key的参数竟然有10个之多,决定缓存Key的条件非常多,即使你用override()方法改变了一下图片的width或者height,也会生成一个完全不同的缓存Key。
EngineKey类的源码大家有兴趣可以自己去看一下,其实主要就是重写了equals()和hashCode()方法,保证只有传入EngineKey的所有参数都相同的情况下才认为是同一个EngineKey对象,
内存缓存
默认情况下,Glide自动就是开启内存缓存的。
skipMemoryCache()方法并传入true,就表示禁用掉Glide的内存缓存功能
非常容易就让人想到LruCache算法(Least Recently Used),也叫近期最少使用算法。它的主要算法原理就是把最近使用的对象用强引用存储在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。LruCache的用法也比较简单
Glide.with(this)
.load(url)
.skipMemoryCache(true)////禁用内存缓存功能
.into(imageView);
硬盘缓存
注意Glide3 中是这样写的
这个diskCacheStrategy()方法基本上就是Glide硬盘缓存功能的一切,它可以接收四种参数:
DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
Glide4中
- 一个是硬盘缓存。
- DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.DATA: 表示只缓存原始图片。
DiskCacheStrategy.RESOURCE: 表示只缓存转换过后的图片。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。
DiskCacheStrategy.AUTOMATIC: 表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)
- DiskCacheStrategy.NONE: 表示不缓存任何内容。
和内存缓存类似,硬盘缓存的实现也是使用的LruCache算法,而且Google还提供了一个现成的工具类DiskLruCache
compile 'com.jakewharton:disklrucache:2.0.2'
当然,Glide是使用的自己编写的DiskLruCache工具类,但是基本的实现原理都是差不多的。
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE) //禁掉硬盘缓存
.into(imageView);
例子
七牛云为了对图片资源进行保护,会在图片url地址的基础之上再加上一个token参数。也就是说,一张图片的url地址可能会是如下格式:
http://url.com/image.jpg?token=d9caa6e02c990b0a
明明是同一张图片,就因为token不断在改变,导致Glide的缓存功能完全失效了。
解决方案了,因为getCacheKey()方法中的逻辑太直白了,直接就是将图片的url地址进行返回来作为缓存Key的。那么其实我们只需要重写这个getCacheKey()方法,加入一些自己的逻辑判断,就能轻松解决掉刚才的问题了。
创建一个MyGlideUrl继承自GlideUrl,代码如下所示:
public class MyGlideUrl extends GlideUrl {
private String mUrl;
public MyGlideUrl(String url) {
super(url);
mUrl = url;
}
//重写这个getCacheKey()方法
@Override
public String getCacheKey() {
return mUrl.replace(findTokenParam(), "");
}
//中token参数的这一部分移除掉
private String findTokenParam() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
可以看到,这里我们重写了getCacheKey()方法,在里面加入了一段逻辑用于将图片url地址中token参数的这一部分移除掉。这样getCacheKey()方法得到的就是一个没有token参数的url地址,从而不管token怎么变化,最终Glide的缓存Key都是固定不变的了。
当然,定义好了MyGlideUrl,我们还得使用它才行,将加载图片的代码改成如下方式即可:
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);
们需要在load()方法中传入这个自定义的MyGlideUrl对象,而不能再像之前那样直接传入url字符串了。不然的话Glide在内部还是会使用原始的GlideUrl类,而不是我们自定义的MyGlideUrl类
Glide的图片变换
百度这张logo图片的尺寸只有540 * 258像素,但是我的手机的分辨率却是1080*1920像素,而我们将ImageView的宽高设置的都是wrap_content,那么图片的宽度应该只有手机屏幕宽度的一半而已,但是这里却充满了全屏,
-
ImageView默认的scaleType是FIT_CENTER。
由于ImageView默认的scaleType是FIT_CENTER,因此会自动添加一个FitCenter的图片变换,而在这个图片变换过程中做了某些操作,导致图片充满了全屏。
Glide给我们提供了专门的API来添加和取消图片变换,想要解决这个问题只需要使用如下代码即可:
Glide.with(this)
.load(url)
.dontTransform()//表示让Glide在加载图片的过程中不进行图片变换,这样刚才调用的applyCenterCrop()、applyFitCenter()就统统无效了。
.into(imageView);
Glide 源码解析
Glide with
RequestManager manager = Glide.with(this);
第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已,然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。
多态都会走到RequestManagerRetriever 实例
RequestManagerRetriever
RequestManagerRetriever类中看似有很多个get()方法的重载,什么Context参数,Activity参数,Fragment参数等等,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。
Application类型 最终调用
非Application类型 最终调用
或
不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的
,
那就是会向当前的Activity当中添加一个隐藏的Fragment
目的:可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
public class RequestManagerRetriever implements Handler.Callback {
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
private volatile RequestManager applicationManager;
...
/**
* Retrieves and returns the RequestManagerRetriever singleton.
返回一个RequestManagerRetriever 单例
*/
public static RequestManagerRetriever get() {
return INSTANCE;
}
// //Glide.with 传入 Application参数 最终所 创建的 RequestManager
private RequestManager getApplicationManager(Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
//其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,
//因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,
//如果应用程序关闭的话,Glide的加载也会同时终止。
// events, we must force the manager to start resumed using ApplicationLifecycle.
applicationManager = new RequestManager(context.getApplicationContext(),
new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
//调用 Application参数的情况 和 非Application参数 来调用生成不同RequestManager
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
//不是Application参数
} 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());
}
}
//调用Application参数 最终所走
return getApplicationManager(context);
}
public RequestManager get(FragmentActivity activity) {
//如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是
//Fragment,都会被强制当成Application来处理。
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;
}
//所有 非 application 参数 最终都会 走 fragmentGet 或 supportFragmentGet
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
// //当前的Activity当中添加一个隐藏的Fragment
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;
}
//所有 非 application 参数 最终都会 走 fragmentGet 或 supportFragmentGet
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
//当前的Activity当中添加一个隐藏的Fragment
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
...
}
RequestManager 类 ]
load多态 这里只分析了 load(String string)
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 load(String string) {
return (DrawableTypeRequest) fromString().load(string);
}
/**
* Returns a request builder that loads data from {@link String}s using an empty signature.
*
*
* 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.
*
*
* @see #from(Class)
* @see #load(String)
*/
public DrawableTypeRequest fromString() {
return loadGeneric(String.class);
}
private DrawableTypeRequest loadGeneric(Class modelClass) {
//这里分别调用了Glide.buildStreamModelLoader()方法和
//Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象
//ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象。
//由于我们刚才传入的参数是String.class,因此最终得到的是StreamStringLoader对象,它是实现了ModelLoader接口的。
ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader 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");
}
//loadGeneric()方法是要返回一个DrawableTypeRequest对象的,
//因此在loadGeneric()方法的最后又去new了一个DrawableTypeRequest对象,然后把刚才获得的ModelLoader对象,还有一大堆杂七杂八的东西都传了进去。
return optionsApplier.apply(
new DrawableTypeRequest(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
...
}
DrawableTypeRequest
rawableTypeRequest中并没有load()方法,那么很容易就能猜想到,load()方法是在父类当中的 DrawableRequestBuilder
public class DrawableTypeRequest extends DrawableRequestBuilder implements DownloadOptions {
private final ModelLoader streamModelLoader;
private final ModelLoader fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static FixedLoadProvider buildProvider(Glide glide,
ModelLoader streamModelLoader,
ModelLoader fileDescriptorModelLoader, Class resourceClass,
Class transcodedClass,
ResourceTranscoder transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader modelLoader = new ImageVideoModelLoader(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider(modelLoader, transcoder, dataLoadProvider);
}
//入口方法
DrawableTypeRequest(Class modelClass, ModelLoader streamModelLoader,
ModelLoader 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 asBitmap() {
// 返回 BitmapTypeRequest ,如果没有进行强制指定的话,那默认就是使用DrawableTypeRequest。
return optionsApplier.apply(new BitmapTypeRequest(this, streamModelLoader,
fileDescriptorModelLoader, optionsApplier));
}
/**
* Attempts to always load the resource as a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*
* 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.
*
*
* @return A new request builder for loading a {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
*/
public GifTypeRequest asGif() {
//返回 GifTypeRequest,如果没有进行强制指定的话,那默认就是使用DrawableTypeRequest。
return optionsApplier.apply(new GifTypeRequest(this, streamModelLoader, optionsApplier));
}
...
}
DrawableRequestBuilder
public class DrawableRequestBuilder
extends GenericRequestBuilder
implements BitmapOptions, DrawableOptions {
DrawableRequestBuilder(Context context, Class modelClass,
LoadProvider loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}
public DrawableRequestBuilder thumbnail(
DrawableRequestBuilder> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder thumbnail(
GenericRequestBuilder, ?, ?, GlideDrawable> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
@Override
public DrawableRequestBuilder thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
@Override
public DrawableRequestBuilder decoder(ResourceDecoder decoder) {
super.decoder(decoder);
return this;
}
@Override
public DrawableRequestBuilder cacheDecoder(ResourceDecoder cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
@Override
public DrawableRequestBuilder encoder(ResourceEncoder encoder) {
super.encoder(encoder);
return this;
}
@Override
public DrawableRequestBuilder priority(Priority priority) {
super.priority(priority);
return this;
}
public DrawableRequestBuilder transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
public DrawableRequestBuilder centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
public DrawableRequestBuilder fitCenter() {
return transform(glide.getDrawableFitCenter());
}
public DrawableRequestBuilder bitmapTransform(Transformation... 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 transform(Transformation... transformation) {
super.transform(transformation);
return this;
}
@Override
public DrawableRequestBuilder transcoder(
ResourceTranscoder transcoder) {
super.transcoder(transcoder);
return this;
}
public final DrawableRequestBuilder crossFade() {
super.animate(new DrawableCrossFadeFactory());
return this;
}
public DrawableRequestBuilder crossFade(int duration) {
super.animate(new DrawableCrossFadeFactory(duration));
return this;
}
public DrawableRequestBuilder crossFade(int animationId, int duration) {
super.animate(new DrawableCrossFadeFactory(context, animationId,
duration));
return this;
}
@Override
public DrawableRequestBuilder dontAnimate() {
super.dontAnimate();
return this;
}
@Override
public DrawableRequestBuilder animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}
@Override
public DrawableRequestBuilder animate(int animationId) {
super.animate(animationId);
return this;
}
@Override
public DrawableRequestBuilder placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
@Override
public DrawableRequestBuilder placeholder(Drawable drawable) {
super.placeholder(drawable);
return this;
}
@Override
public DrawableRequestBuilder fallback(Drawable drawable) {
super.fallback(drawable);
return this;
}
@Override
public DrawableRequestBuilder fallback(int resourceId) {
super.fallback(resourceId);
return this;
}
@Override
public DrawableRequestBuilder error(int resourceId) {
super.error(resourceId);
return this;
}
@Override
public DrawableRequestBuilder error(Drawable drawable) {
super.error(drawable);
return this;
}
@Override
public DrawableRequestBuilder listener(
RequestListener super ModelType, GlideDrawable> requestListener) {
super.listener(requestListener);
return this;
}
@Override
public DrawableRequestBuilder diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}
@Override
public DrawableRequestBuilder skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}
@Override
public DrawableRequestBuilder override(int width, int height) {
super.override(width, height);
return this;
}
@Override
public DrawableRequestBuilder sourceEncoder(Encoder sourceEncoder) {
super.sourceEncoder(sourceEncoder);
return this;
}
@Override
public DrawableRequestBuilder dontTransform() {
super.dontTransform();
return this;
}
@Override
public DrawableRequestBuilder signature(Key signature) {
super.signature(signature);
return this;
}
//DrawableRequestBuilder类中有一个into()方法
//也就是说,最终load()方法返回的其实就是一个DrawableTypeRequest对象
@Override
public DrawableRequestBuilder load(ModelType model) {
super.load(model);
return this;
}
@Override
public DrawableRequestBuilder clone() {
return (DrawableRequestBuilder) super.clone();
}
@Override
public Target into(ImageView view) {
return super.into(view);
}
@Override
void applyFitCenter() {
fitCenter();
}
@Override
void applyCenterCrop() {
centerCrop();
}
}
into()
因为into()方法也是整个Glide图片加载流程中逻辑最复杂的地方。
into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。DrawableRequestBuilder的父类是GenericRequestBuilder,
GenericRequestBuilder类中的into()方法,
public Target 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.
}
}
//glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,
//Target对象则是用来最终展示图片用的
return into(glide.buildImageViewTarget(view, transcodeClass));
}
如果我们跟进去的话会看到如下代码:
Target buildImageViewTarget(ImageView imageView, Class transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
这里其实又是调用了ImageViewTargetFactory的buildTarget()方法,我们继续跟进去,代码如下所示:
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public Target buildTarget(ImageView view, Class clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
//基本都是返回这个
return (Target) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。
这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。
至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。
通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行
public > 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();
}
//调用buildRequest()方法构建出了一个Request对象
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
//执行这个Request。
requestTracker.runRequest(request);
return target;
}
Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
private Request buildRequest(Target target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
//内部其实又调用了buildRequestRecursive()方法
//90%的代码都是在处理缩略图的。如果我们只追主线流程的话.只看最后一句就行了
private Request buildRequestRecursive(Target 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 {
// 这里调用了obtainRequest()方法来获取一个Request对象
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
//最终 执行的 是这个
//obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,
//而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。
//因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。
private Request obtainRequest(Target 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);
}
那么我们进入到这个GenericRequest的obtain()方法瞧一瞧:
public final class GenericRequest implements Request, SizeReadyCallback,
ResourceCallback {
...
public static GenericRequest obtain(
LoadProvider loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target target,
float sizeMultiplier,
Drawable placeholderDrawable,
int placeholderResourceId,
Drawable errorDrawable,
int errorResourceId,
Drawable fallbackDrawable,
int fallbackResourceId,
RequestListener super A, R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
Transformation transformation,
Class transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest request = (GenericRequest) REQUEST_POOL.poll();
if (request == null) {
//这里 new了一个GenericRequest对象 ,并在最后一行返回,
request = new GenericRequest();
}
//调用了GenericRequest的init(),里面主要就是一些赋值的代码,
//将传入的这些参数赋值到GenericRequest的成员变量当中
request.init(loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderResourceId,
errorDrawable,
errorResourceId,
fallbackDrawable,
fallbackResourceId,
requestListener,
requestCoordinator,
engine,
transformation,
transcodeClass,
isMemoryCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
//最后 返回这个 request
return request;
}
...
}
好,那现在解决了构建Request对象的问题,接下来我们看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
//判断Glide 是不是处于暂停状态
if (!isPaused) {
request.begin();
} else {
//否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
pendingRequests.add(request);
}
}
我们重点来看这个begin()方法。由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:
@Override
public void begin() {
startTime = LogTime.getLogTime();
//model也就是我们在第二步load()方法中传入的图片URL地址,
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
//具体的图片加载
//一种是你使用了override() API为图片指定了一个固定的宽高,
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//没指定的话,就会执行调用target.getSize()方法
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
//调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
你跟到onException()方法里面去看看,你会发现它最终会调用到一个setErrorPlaceholder()当中,如下所示:
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用target.onLoadFailed()方法并将占位图传入。那么onLoadFailed()方法中做了什么呢?
public abstract class ImageViewTarget extends ViewTarget implements GlideAnimation.ViewAdapter {
...
@Override
public void onLoadStarted(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setImageDrawable(errorDrawable);
}
...
}
很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的placeholder()和error()这两个占位图API底层的实现原理。
继续回到begin()方法。
这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。如果没指定的话,就会执行第12行代码,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高 总之在计算完之后,它也会调用onSizeReady()方法。也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,在这里进行下一步操作。那么我们跟到这个方法里面来:
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
//调用了loadProvider.getModelLoader()方法
ModelLoader modelLoader = loadProvider.getModelLoader();
final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}