Glide的基本使用
看一下郭神的博客,Android图片加载框架最全解析(八),带你全面了解Glide 4的用法
图片加载机制的基本流程
我们从这行代码看起
Glide.with(this).load(url).into(imageView);
1. with()
Glide会根据我们传入with()方法的参数来确定图片加载的生命周期。(避免消耗多余的资源,也避免在Activity销毁后加载图片从而导致空指针问题)
是Glide类中一组静态方法,有好几个方法重载,都是先调用RequestManagerRetriever的静态方法get()得到一个RequestManagerRetriever对象(此为单例),再调用这个实例的get()方法来获取RequestManager对象
RequestManagerFragment创建一个ActivityFragmentLifecycle实现了Lifecycle接口。所有的LifecycleListener会添加到一个集合中,当RequestManagerFragment生命周期方法触发时,会调用ActivityFragmentLifecycle相应生命周期方法,这个方法然后再遍历调用所有LifecycleListener的生命周期方法
RequestManager主要作用为实现request和Activity生命周期相关联,并且还实现了LifecycleListener接口来处理请求的生命周期
public class Glide {
...
// RequestManager: 实现Glide加载资源的管理类,根据Activity生命周期管控资源加载请求,可以开启、暂停及重启。
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);
}
}
所以来看实例的get方法
RequestManagerRetriever # get()
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.
// 通常,暂停/恢复由我们添加到fragment或activity中的fragment来处理。
// 但是,在这种情况下,由于附加到应用程序的管理器将不接收生命周期事件,我们必须强制管理器开始使用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 {
// 如果activity被摧毁,抛出异常
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
// fragment和activity未关联
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 {
// getFragmentManager()所得到的是所在fragment 的父容器的管理器,
// getChildFragmentManager()所得到的是在fragment 里面子容器的管理器。
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);
}
}
android.app.Fragment:使用getFragmentManager()方法。
android.support.v4.app.Fragment:使用getSupportFragmentManager()方法。
走的非Application方法,最终都调用如下两个方法:
RequestManagerRetriever # fragmentGet()
添加android.app.Fragment
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
// 在当前activity中创建一个隐藏的fragment并add到当前activity中
// RequestManagerFragment继承Fragment
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
// 将requestManager与fragment绑定
current.setRequestManager(requestManager);
}
return requestManager;
}
@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);
// 在当前activity中创建一个隐藏的fragment并add到当前activity中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
RequestManagerRetriever # supportFragmentGet()
添加v4包的Fragment
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;
}
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;
}
以上get()方法的重载可以分为两种情况:传入Application类型的参数和传入非Application类型的参数
Application类型的参数
调用带有Context参数的get()方法重载,然后调用getApplicationManager()来获取RequestManager对象(应用程序管理器)。
这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,Glide不需要做什么特殊处理,它自动是和应用程序的生命周期是同步的,应用程序关闭,Glide加载也会同时终止
非Application类型的参数
如果不在主线程或者Android SDK版本过低,走的还是传入Application的方法。
不管是Activity、Fragment还是FragmentActivity,最终流程都是一样的,那就是会向当前Activity当中添加一个隐藏的Fragment。因为Glide需要知道加载的生命周期。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
2. load()
Glide是支持图片URL字符串、图片本地路径等等加载形式的,因此RequestManager中也有很多个load()方法的重载。
先来看加载图片URL字符串的load()方法。
/**
* Returns a request builder to load the given {@link java.lang.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 com.bumptech.glide.GenericRequestBuilder#signature(com.bumptech.glide.load.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 com.bumptech.glide.load.engine.DiskCacheStrategy#NONE} and/or
* {@link com.bumptech.glide.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对象
// 用于将任意复杂数据模型转换为可使用的具体数据类型
// 通过{@link datafetcher}获取模型所表示的资源的数据
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));
}
ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象。由于刚才传进的参数是String.class,所以buildStreamModelLoader()方法最后得到的是StreamStringLoader对象(实现ModelLoader接口)
在loadGeneric方法的最后new了一个DrawableTypeRequest对象。
DrawableTypeRequest
提供了asBitmap()(强制指定加载静态图片)和asGif()两个方法,它们分别创建了一个BitmapTypeRequest和GifTypeRequest,如果没有强制指定,默认使用DrawableTypeRequest
// 用于创建加载请求的类,该类可以直接加载动画gif绘图文件或位图绘图文件,
// 或者添加{@link com.bumptech.glide.load.resource.transcode.resourcetocoder}以将数据转换为{@link android.graphics.drawable.drawable}以外的资源类型。
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 com.bumptech.glide.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));
}
/**
* {@inheritDoc}
*/
public <Y extends Target<File>> Y downloadOnly(Y target) {
return getDownloadOnlyRequest().downloadOnly(target);
}
/**
* {@inheritDoc}
*/
public FutureTarget<File> downloadOnly(int width, int height) {
return getDownloadOnlyRequest().downloadOnly(width, height);
}
private GenericTranscodeRequest<ModelType, InputStream, File> getDownloadOnlyRequest() {
return optionsApplier.apply(new GenericTranscodeRequest<ModelType, InputStream, File>(File.class, this,
streamModelLoader, InputStream.class, File.class, optionsApplier));
}
}
再次回到RequestManager的load方法,fromString()方法会返回一个DrawableTypeRequest对象,接下来调用这个对象的load()方法,可是上面DrawableTypeRequest的源码中并没有load方法,因此应该是在它的父类DrawableRequestBuilder中。
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();
}
/**
* Loads and displays the {@link GlideDrawable} retrieved by the given thumbnail request if it finishes before this
* request. Best used for loading thumbnail {@link GlideDrawable}s that are smaller and will be loaded more quickly
* than the fullsize {@link GlideDrawable}. There are no guarantees about the order in which the requests will
* actually finish. However, if the thumb request completes after the full request, the thumb {@link GlideDrawable}
* will never replace the full image.
*
* @see #thumbnail(float)
*
* <p>
* Note - Any options on the main request will not be passed on to the thumbnail request. For example, if
* you want an animation to occur when either the full {@link GlideDrawable} loads or the thumbnail loads,
* you need to call {@link #animate(int)} on both the thumb and the full request. For a simpler thumbnail
* option where these options are applied to the humbnail as well, see {@link #thumbnail(float)}.
* </p>
*
* <p>
* Only the thumbnail call on the main request will be obeyed, recursive calls to this method are ignored.
* </p>
*
* @param thumbnailRequest The request to use to load the thumbnail.
* @return This builder object.
*/
public DrawableRequestBuilder<ModelType> thumbnail(
DrawableRequestBuilder<?> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> thumbnail(
GenericRequestBuilder<?, ?, ?, GlideDrawable> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
super.decoder(decoder);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
super.encoder(encoder);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> priority(Priority priority) {
super.priority(priority);
return this;
}
/**
* Transform {@link GlideDrawable}s using the given
* {@link com.bumptech.glide.load.resource.bitmap.BitmapTransformation}s.
*
* <p>
* Note - Bitmap transformations will apply individually to each frame of animated GIF images and also to
* individual {@link Bitmap}s.
* </p>
*
* @see #centerCrop()
* @see #fitCenter()
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @param transformations The transformations to apply in order.
* @return This request builder.
*/
public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
/**
* Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
*
* @see #fitCenter()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
*/
@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
/**
* Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
*
* @see #centerCrop()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
*/
@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> fitCenter() {
return transform(glide.getDrawableFitCenter());
}
/**
* Transform {@link GlideDrawable}s using the given {@link android.graphics.Bitmap} transformations. Replaces any
* previous transformations.
*
* @see #fitCenter()
* @see #centerCrop()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
*/
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);
}
/**
* {@inheritDoc}
*
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #centerCrop()
* @see #fitCenter()
*/
@Override
public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
super.transform(transformation);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> transcoder(
ResourceTranscoder<GifBitmapWrapper, GlideDrawable> transcoder) {
super.transcoder(transcoder);
return this;
}
/**
* {@inheritDoc}
*/
public final DrawableRequestBuilder<ModelType> crossFade() {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
return this;
}
/**
* {@inheritDoc}
*/
public DrawableRequestBuilder<ModelType> crossFade(int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(duration));
return this;
}
/**
* {@inheritDoc}
*/
@Deprecated
public DrawableRequestBuilder<ModelType> crossFade(Animation animation, int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(animation, duration));
return this;
}
/**
* {@inheritDoc}
*/
public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>(context, animationId,
duration));
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> dontAnimate() {
super.dontAnimate();
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> animate(ViewPropertyAnimation.Animator animator) {
super.animate(animator);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
/**
* {@inheritDoc}
*/
@Deprecated
@SuppressWarnings("deprecation")
@Override
public DrawableRequestBuilder<ModelType> animate(Animation animation) {
super.animate(animation);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
/**
* {@inheritDoc}
*/
@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;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> error(Drawable drawable) {
super.error(drawable);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> listener(
RequestListener<? super ModelType, GlideDrawable> requestListener) {
super.listener(requestListener);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> diskCacheStrategy(DiskCacheStrategy strategy) {
super.diskCacheStrategy(strategy);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> skipMemoryCache(boolean skip) {
super.skipMemoryCache(skip);
return this;
}
/**
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> override(int width, int height) {
super.override(width, height);
return this;
}
// ...
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
// 父类的load()设置一些参数值
super.load(model);
return this;
}
@Override
public DrawableRequestBuilder<ModelType> clone() {
return (DrawableRequestBuilder<ModelType>) super.clone();
}
/**
* {@inheritDoc}
*
* <p>
* Note - If no transformation is set for this load, a default transformation will be applied based on the
* value returned from {@link android.widget.ImageView# getScaleType()}. To avoid this default transformation,
* use {@link #dontTransform()}.
* </p>
*
* @param view {@inheritDoc}
* @return {@inheritDoc}
*/
// 默认转换是ImageView# getScaleType(),不用默认转换dontTransform()
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
@Override
void applyFitCenter() {
fitCenter();
}
@Override
void applyCenterCrop() {
centerCrop();
}
}
里面的很多方法比如placeholder()、error()、diskCacheStrategy()方法等是我们经常用到过的,这些就是Glide的绝大多数API了。
最终load()方法就是返回一个DrawableTypeRequest对象。
3. into()
最主要的部分。刚刚在DrawableRequestBuilder类中出现了into方法,里面只有一句super.into(view),我们来看它的父类GenericRequestBuilder中的into方法。
总体逻辑
GenericRequestBuilder # into()
/**
* Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into the view, and frees
* any resources Glide may have previously loaded into the view so they may be reused.
*
* @see Glide#clear(android.view.View)
*
* @param view The view to cancel previous loads for and load the new resource into.
* @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
*/
//
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方法。
Glide # buildImageViewTarget()
创建的Target对象是用来最终展示图片。
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
把我们传入的imageView包装成一个Target。
ImageViewTargetFactory # buildTarget()
创建并返回与图片来源对应的imageViewTarget。buildTarget()方法会根据传入的class参数来构建不同的Target对象。class参数基本只有两种情况,如果使用Glide加载图片的时候调用了asBitmap(),那么这里就得到BitmapImageViewTarget对象,否则得到的都是GlideDrawableImageViewTarget对象。而DrawableImageViewTarget通常用不到。
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)");
}
}
}
总结:也就是说glide.buildImageViewTarget方法最后返回的是GlideDrawableImageViewTarget对象,最后一行是把这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。
GenericRequestBuilder # into()
/**
* Set the target the resource will be loaded into.
*
* @see Glide#clear(com.bumptech.glide.request.target.Target)
*
* @param target The target to load the resource into.
* @return The given target.
*/
// 设置资源将加载到的目标
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");
}
// 确保数据来源已确定,即已经调用了load()方法
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
// 清理target之前的request
if (previous != null) {
previous.clear();
// 停止绑定到当前target的上一个request的图片请求处理
requestTracker.removeRequest(previous);
previous.recycle();
}
// 创建Request对象
Request request = buildRequest(target);
// 与target进行绑定
target.setRequest(request);
lifecycle.addListener(target);
// 执行这个Request
requestTracker.runRequest(request);
return target;
}
Request是用来发出加载图片请求的,它是非常关键的一个组件。
上述方法的步骤:
- 如果当前target中的request对象存在,则清空并终止这个request对象的执行
- 创建新的request对象并与当前target绑定
- 执行创建的图片处理请求request
在没有Glide之前,我们处理ListView中的图片加载其实是一件比较麻烦的事情。由于ListView中Item的复用机制,会导致网络图片加载的错位或者闪烁。那我们解决这个问题的办法也很简单,就是给当前的ImageView设置tag,这个tag可以是图片的URL等等。当从网络中获取到图片时判断这个ImageVIew中的tag是否是这个图片的URL,如果是就加载图片,如果不是则跳过。在有了Glide之后,我们处理ListView或者Recyclerview中的图片加载就很无脑了,根本不需要作任何多余的操作,直接正常使用就行了。这其中的原理是Glide给我们处理了这些判断,我们来看一下Glide内部是如何处理的:
可以看出本质还是setTag()和getTag()
@Override
public Request getRequest() {
Object tag = getTag();
Request request = null;
if (tag != null) {
if (tag instanceof Request) {
request = (Request) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
}
}
return request;
}
private void setTag(Object tag) {
if (tagId == null) {
isTagUsedAtLeastOnce = true;
view.setTag(tag);
} else {
view.setTag(tagId, tag);
}
}
GenericRequestBuilder # buildRequest()
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
// 会针对是否有缩略图来创建不同尺寸的请求
// 缩略图采用的是ThumbnailRequestCoordinator类
// 缩略图可以使用RequestBuilder.thumbnail(…)方法来添加上。
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);
// ThumbnailRequestCoordinator
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);
// ThumbnailRequestCoordinator
return coordinator;
} else {
// Base case: no thumbnail.
// 没有缩略图
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
// 通常情况下,通过这个方法来获得request
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
// 刚才在load()方法中调用的所有API都是在这里组装到Request对象当中的
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()
由此可以看出obtain()方法最后得到的是一个GenericRequest对象
public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(
LoadProvider<A, T, Z, R> loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target<R> 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<Z> transformation,
Class<R> transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory<R> animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
if (request == null) {
request = new GenericRequest<A, T, Z, R>();
}
// 主要是赋值,把传入的参数赋给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);
return request;
}
到此,Request的创建就完成了,再回到之前的方法GenericRequestBuilder 的 into(),看request对象是怎么执行的,紧接着它里面的方法runRequest()
RequestTracker # runRequest()
入口,RequestTracker这个类是所有请求操作的真正处理者,所有Request的暂停取消执行操作都由RequestTracker来完成,它是RequestManager里面的成员变量。加载前
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
// 请求set集合
requests.add(request);
// 判断Glide当前是不是暂停状态
if (!isPaused) {
// 执行request
request.begin();
} else {
// 如果是暂停状态先将request加入到执行队列中
// 暂停状态解除了再执行
pendingRequests.add(request);
}
}
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 {
// 获取尺寸后也会调用onSizeReady()
// 内部是根据ImageView的layout_width和layout_height值做一系列计算图片应该的宽高
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
// 先显示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,ImageViewTarget的这个方法就是把error这张占位图显示到imageview中。
GenericRequest # 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);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); // ①
// ImageVideoFetcher
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height); // ②
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder(); // ①
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
// 将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列值都传进去
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));
}
}
先来看loadProvider是什么,回到源头,先回到第二步load()方法,它最后返回的是DrawableTypeRequest对象,我们现在来分析一下它的构造函数
1. DrawableTypeRequest
buildProvider()方法中的streamModelLoader, fileDescriptorModelLoader都是loadGeneric()方法中构建出来的
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;
}
DrawableTypeRequest # buildProvider()
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) {
// glide.buildTranscoder用来构建一个ResourceTranscoder,它是用于对图片进行转码的
// ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
// glide.buildDataProvider用来构建一个DataLoadProvider,它是用于对图片进行编解码的
// DataLoadProvider也是一个接口,实际会构建出一个ImageVideoGifDrawableLoadProvider对象
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
// 把之前loadGeneric()方法中构建的两个ModelLoader封装到了ImageVideoModelLoader中
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
// 把刚才构建出来的GifBitmapWrapperDrawableTranscoder、ImageVideoGifDrawableLoadProvider、ImageVideoModelLoader都封装进去
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
最后返回的这个FixedLoadProvider就是onSizeReady()中的loadProvider。
再回到onSizeReady()方法,看loadProvider.getModelLoader()和getTranscoder(),它们得到的对象就是刚才我们分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder
2. ImageVideoModelLoader # getResourceFetcher()
得到的是一个ImageVideoFetcher
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
DataFetcher<InputStream> streamFetcher = null;
if (streamLoader != null) {
// 这个streamLoader就是在loadGeneric()中构建出来的StreamStringLoader(load()传入参数是string的话)
// 这个方法会得到一个HttpUrlFetcher对象
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
// 将HttpUrlFetcher传入
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
3. Engine # load()
大多数都是在处理缓存,EngineJob的主要作用是用来开启线程的,为后面的异步加载图片做准备
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
// 这个 Key 用来标识一个请求,Key 相同的 Request 是等效的。可以看出这个 Key 有多个参数组成,只要其中一个参数不同,Key 就不同
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// 避免重复请求,因为如果多个请求如果是等效的,没有必要为它们去加载多次,而只需要加载结果到来时逐个通知它们一遍就好
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// 构建了一个EngineJob,它的主要作用是用来开启线程的,为后面的异步加载图片做准备
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
//
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
// EngineRunnable的manager就是EngineJob对象
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
//
engineJob.addCallback(cb);
// 运行EngineRunnable对象,让它的run方法在子线程中执行(扔到线程池)
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
EngineRunnable # run()
得到Resource对象
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode(); // ①
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
EngineRunnable # decode()
private Resource<?> decode() throws Exception {
// 从缓存中decode图片就会执行decodeFromCache
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
// 返回的是Resource<Z>
return decodeFromSource(); // ①
}
}
EngineRunnable # decodeFromSource()
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
DecodeJob # decodeFromSource()
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
// 后面
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
// fetcher就是刚才在onSizeReady()方法中得到的ImageVideoFetcher对象
final A data = fetcher.loadData(priority); // ①
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
// 把返回的ImageVideoWrapper对象传入
decoded = decodeFromSourceData(data); // ②
} finally {
fetcher.cleanup();
}
return decoded;
}
1. ImageVideoModelLoader.ImageVideoFetcher # loadData()
返回ImageVideoWrapper对象
@SuppressWarnings("resource")
// @see ModelLoader.loadData
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
// priority表示load优先级
InputStream is = null;
if (streamFetcher != null) {
try {
// 这里的streamFetcher是刚才在组装ImageVideoFetcher对象传进来的HttpUrlFetcher,这里开始真正的网络请求
is = streamFetcher.loadData(priority); // ①
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
// 把网络请求获取的InputStream传进去,返回上一层
return new ImageVideoWrapper(is, fileDescriptor);
}
HttpUrlFetcher # loadData()
网络请求的入口,返回InputStream,服务器返回的数据在哪开始读呢?返回到ImageVideoFetcher的loadData()方法里
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new IOException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
2. DecodeJob # decodeFromSourceData()
返回的是Resource对象,其实是Resource对象(else情况下)
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
// 对图片进行解码
// loadProvider是刚才在onSizeReady()方法中得到的FixedLoadProvider
// getSourceDecoder()会调用ImageVideoGifDrawableLoadProvider的getSourceDecoder()
// 而getSourceDecoder()得到的则是一个GifBitmapWrapperResourceDecoder对象
decoded = loadProvider.getSourceDecoder().decode(data, width, height); // ①
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
GifBitmapWrapperResourceDecoder # decode()
对图片进行解码
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes); //
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
result = decodeStream(source, width, height, bytes); //
} else {
result = decodeBitmapWrapper(source, width, height);
}
return result;
}
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
throws IOException {
InputStream bis = streamFactory.build(source.getStream(), bytes);
bis.mark(MARK_LIMIT_BYTES);
// 会先从流中读取2个字节的数据判断图片的类型
ImageHeaderParser.ImageType type = parser.parse(bis);
bis.reset();
GifBitmapWrapper result = null;
if (type == ImageHeaderParser.ImageType.GIF) {
result = decodeGifWrapper(bis, width, height);
}
// Decoding the gif may fail even if the type matches.
if (result == null) {
// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
// pass in a new source containing the buffered stream rather than the original stream.
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
// 普通的静图
result = decodeBitmapWrapper(forBitmapDecoder, width, height); // ①
}
// 返回GifBitmapWrapper对象
return result;
}
GifBitmapWrapperResourceDecoder # decodeBitmapWrapper()
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
// 这里的bitmapDecoder是一个ImageVideoBitmapDecoder对象
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height); // ①
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null); // ②
}
return result;
}
ImageVideoBitmapDecoder # decode()
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException {
Resource<Bitmap> result = null;
InputStream is = source.getStream();
if (is != null) {
try {
// streamDecoder是一个StreamBitmapDecoder对象
result = streamDecoder.decode(is, width, height); // 解码 ①
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
}
}
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
// StreamBitmapDecoder # decode()
// 返回BitmapResource对象
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat); // ①
return BitmapResource.obtain(bitmap, bitmapPool);
}
Downsampler # decode()
对服务器返回的InputStream的读取以及对图片的加载都在这里,进行了对图片的压缩、还有旋转、圆角等逻辑处理,返回的是bitmap对象,接下来就是让这个bitmap显示在界面上,回到上一步
/**
* Load the image for the given InputStream. If a recycled Bitmap whose dimensions exactly match those of the image
* for the given InputStream is available, the operation is much less expensive in terms of memory.
*
* <p>
* Note - this method will throw an exception of a Bitmap with dimensions not matching
* those of the image for the given InputStream is provided.
* </p>
*
* @param is An {@link InputStream} to the data for the image.
* @param pool A pool of recycled bitmaps.
* @param outWidth The width the final image should be close to.
* @param outHeight The height the final image should be close to.
* @return A new bitmap containing the image from the given InputStream, or recycle if recycle is not null.
*/
// 为给定的inputstream加载图像。如果可以使用尺寸与给定inputstream的图像尺寸完全匹配的回收位图,则该操作在内存方面要便宜得多
@SuppressWarnings("resource")
// see BitmapDecoder.decode
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
// if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still attempt to decode the
// full image even when the header decode fails and/or overflows our read buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
// ①
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
public int[] getDimensions(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
options.inJustDecodeBounds = true;
decodeStream(is, bufferedStream, options);
options.inJustDecodeBounds = false;
return new int[] { options.outWidth, options.outHeight };
}
// 压缩图片
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config config = getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
options.inPreferredConfig = config;
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
// This is large, but jpeg headers are not size bounded so we need something large enough to minimize
// the possibility of not being able to fit enough of the header in the buffer to get the image size so
// that we don't fail to load images. The BufferedInputStream will create a new buffer of 2x the
// original size each time we use up the buffer space without passing the mark so this is a maximum
// bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
is.mark(MARK_POSITION);
} else {
// 一旦我们读取了图像头,就不再需要允许缓冲区扩大大小。避免
// 读取图像数据的不必要分配,我们修复标记限制,使其不大于
// 当前缓冲区大小。
// Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
// unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
// current buffer size here. See issue #225.
bufferedStream.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
try {
if (options.inJustDecodeBounds) {
is.reset();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
+ " sample=" + options.inSampleSize, e);
}
}
return result;
}
BitmapResource
把bitmap对象包装成了BitmapResource对象,如果我们想获取bitmap,只需要调用Resource的get()方法
public class BitmapResource implements Resource<Bitmap> {
private final Bitmap bitmap;
private final BitmapPool bitmapPool;
/**
* Returns a new {@link BitmapResource} wrapping the given {@link Bitmap} if the Bitmap is non-null or null if the
* given Bitmap is null.
*
* @param bitmap A Bitmap.
* @param bitmapPool A non-null {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool}.
*/
public static BitmapResource obtain(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
return null;
} else {
return new BitmapResource(bitmap, bitmapPool);
}
}
public BitmapResource(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
throw new NullPointerException("Bitmap must not be null");
}
if (bitmapPool == null) {
throw new NullPointerException("BitmapPool must not be null");
}
this.bitmap = bitmap;
this.bitmapPool = bitmapPool;
}
@Override
public Bitmap get() {
return bitmap;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
if (!bitmapPool.put(bitmap)) {
bitmap.recycle();
}
}
}
我们一步步返回,最后将Resource返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法。
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
// 将Resource<Bitmap>封装到GifBitmapWrapper中
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
这个GifBitmapWrapper既能封装GIF,又能封装Bitmap,保证不管是什么类型的图片Glide都能从容应对。
GifBitmapWrapper就是分别对gifResource和bitmapResource做了一层封装
再进行返回到GifBitmapWrapperResourceDecoder最外层的decode(),会再进行一次封装。
封装成GifBitmapWrapperResource,最终返回的是Resource对象。这个GifBitmapWrapperResource和刚才的BitmapResource是相似的,都实现的Resource接口,都可以通过get()方法来获取封装起来的具体内容
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
// 返回的GifBitmapWrapper对象
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null; // ①
}
GifBitmapWrapperResource
public class GifBitmapWrapperResource implements Resource<GifBitmapWrapper> {
private final GifBitmapWrapper data;
public GifBitmapWrapperResource(GifBitmapWrapper data) {
if (data == null) {
throw new NullPointerException("Data must not be null");
}
this.data = data;
}
@Override
public GifBitmapWrapper get() {
return data;
}
@Override
public int getSize() {
return data.getSize();
}
@Override
public void recycle() {
Resource<Bitmap> bitmapResource = data.getBitmapResource();
if (bitmapResource != null) {
bitmapResource.recycle();
}
Resource<GifDrawable> gifDataResource = data.getGifResource();
if (gifDataResource != null) {
gifDataResource.recycle();
}
}
}
最终回到DecodeJob中的decodeFromSource()
public Resource<Z> decodeFromSource() throws Exception {
// Resource<GifBitmapWrapper>对象
Resource<T> decoded = decodeSource();
// Resource<T>转成Resource<Z>
return transformEncodeAndTranscode(decoded); // ①
}
DecodeJob # transformEncodeAndTranscode()
返回Resource对象
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
//
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
// Resource<T>转成Resource<Z> ①
// Resource<Z>就是Resource<GlideDrawable>
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
transcode()方法调用了transcoder的transcode()方法。transcoder是什么呢?
在第二步load()方法返回的那个DrawableTypeRequest对象,它的构建函数中去构建了一个FixedLoadProvider对象,然后我们将三个参数传入到了FixedLoadProvider当中,其中就有一个GifBitmapWrapperDrawableTranscoder对象。后来在onSizeReady()方法中获取到了这个参数,并传递到了Engine当中,然后又由Engine传递到了DecodeJob当中。所以也就是说transcoder就是GifBitmapWrapperDrawableTranscoder
GifBitmapWrapperDrawableTranscoder # transcode()
核心作用就是用来转码的,因为GifBitmapWrapper是无法直接显示到ImageView上面的,只有Bitmap或Drawable才能显示到ImageView上。这里transcode()先从Resource中取出GifBitmapWrapper对象,然后再从GifBitmapWrapper中取出Resource对象。如果Resource为空,说明此时加载的是GIF图,调用getGifResource()将图片取出(因为Glide用于加载GIF图片是使用的GifDrawable这个类,它本身就是一个Drawable对象了)
如果Resource不为空,需要再做一次转码,将Bitmap对象转换成Drawable对象才行(因为要保证静图和动图的类型一致性)
@SuppressWarnings("unchecked")
@Override
public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) {
GifBitmapWrapper gifBitmap = toTranscode.get();
Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource();
final Resource<? extends GlideDrawable> result;
if (bitmapResource != null) {
// 调用GlideBitmapDrawableTranscoder.transcode()
result = bitmapDrawableResourceTranscoder.transcode(bitmapResource); // ①
} else {
result = gifBitmap.getGifResource();
}
// This is unchecked but always safe, anything that extends a Drawable can be safely cast to a Drawable.
return (Resource<GlideDrawable>) result;
}
GlideBitmapDrawableTranscoder # transcode()
new了一个GlideBitmapDrawable对象,并把Bitmap封装到里面,再对GlideBitmapDrawable再进行一次封装,返回一个Resource对象
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
private final Resources resources;
private final BitmapPool bitmapPool;
public GlideBitmapDrawableTranscoder(Context context) {
this(context.getResources(), Glide.get(context).getBitmapPool());
}
public GlideBitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) {
this.resources = resources;
this.bitmapPool = bitmapPool;
}
@Override
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
...
}
现在再返回到GifBitmapWrapperDrawableTranscoder的transcode()方法中,发现不管是静图的Resource还是动图的Resource对象,它们都是属于父类Resource对象的,GifBitmapWrapperDrawableTranscoder的transcode()最后返回的就是Resource,这个就是转换后的Resource
一直向上返回到EngineRunnable的run()方法。
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
// 获得到Resource<GlideDrawable>
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
// 表示图片加载已经完成
onLoadComplete(resource);
}
}
EngineRunnable # onLoadComplete()
private void onLoadComplete(Resource resource) {
// 这个manager就是EngineJob对象
manager.onResourceReady(resource);
}
EngineJob # onResourceReady()
在onResourceReady()方法里使用handler发出了一条MSG_COMPLETE消息,然后在MainThreadCallback的handleMessage()中会收到这条消息,从这里又回到主线程,很快就更新UI。
handleResultOnMainThread()方法中又通过一个循环,调用了所有ResourceCallback的onResourceReady()方法。ResourceCallback是什么呢?在addCallback方法中(向cbs集合中去添加ResourceCallback)
class EngineJob implements EngineRunnable.EngineRunnableManager {
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
private final List<ResourceCallback> cbs = new ArrayList<ResourceCallback>();
...
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
cb.onResourceReady(engineResource);
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
// 不是此类中的
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
@Override
public void onException(final Exception e) {
this.exception = e;
MAIN_THREAD_HANDLER.obtainMessage(MSG_EXCEPTION, this).sendToTarget();
}
private void handleExceptionOnMainThread() {
if (isCancelled) {
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received an exception without any callbacks to notify");
}
hasException = true;
listener.onEngineJobComplete(key, null);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
cb.onException(exception);
}
}
}
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
...
}
addCallback()方法在Engine的load方法里面
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority,
boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
...
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
这个cb参数是从哪传进来的?调用Engine的load方法是GenericRequest的onSizeReady()方法
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
ResourceCallback {
...
@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);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
// 最后一个参数传的this
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));
}
}
...
}
GenericRequest本身就实现了ResourceCallback的接口,因此EngineJob的回调最终其实就是回调到了GenericRequest的onResourceReady()方法当中了
GenericRequest # onResourceReady()
第一个onResourceReady()方法当中调用resource.get()获取到了封装的图片对象,也就是GlideBitmapDrawable对象或者是GifDrawable对象,这个值传入到了第二个onResourceReady()方法中
/**
* A callback method that should never be invoked directly.
*/
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("Expected to receive an object of " + transcodeClass
+ " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
+ " inside Resource{" + resource + "}."
+ (received != null ? "" : " "
+ "To indicate failure return a null Resource object, "
+ "rather than a Resource object containing null data.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
/**
* Internal {@link #onResourceReady(Resource)} where arguments are known to be safe.
*
* @param resource original {@link Resource}, never <code>null</code>
* @param result object returned by {@link Resource#get()}, checked for type and never <code>null</code>
*/
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);// ①
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
这个target是什么?在第三步into()方法的最后一行,调用了glide.buildImageViewTarget()方法来构建出一个Target,而这个Target就是一个GlideDrawableImageViewTarget对象。
GlideDrawableImageViewTarget
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
private static final float SQUARE_RATIO_MARGIN = 0.05f;
private int maxLoopCount;
private GlideDrawable resource;
public GlideDrawableImageViewTarget(ImageView view) {
this(view, GlideDrawable.LOOP_FOREVER);
}
public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
super(view);
this.maxLoopCount = maxLoopCount;
}
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
@Override
public void onStart() {
if (resource != null) {
resource.start();
}
}
@Override
public void onStop() {
if (resource != null) {
resource.stop();
}
}
}
里面的onResourceReady()方法中做了一些逻辑处理,包括如果是GIF图片的话,就调用resource.start()方法开始播放图片,但是好像并没有看到哪里有将GlideDrawable显示到ImageView上的逻辑。确实没有,不过父类里面有,这里调用了super.onResourceReady()方法。
GlideDrawableImageViewTarget的父类是ImageViewTarget
ImageViewTarget # onResourceReady()
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
...
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
}
ImageViewTarget的setResource()是一个抽象方法,具体的实现还在子类中,再来看一下GlideDrawableImageViewTarget的setResource()方法
GlideDrawableImageViewTarget # setResource()
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
至此,图片就显示到View上了