Glide和Picasso的对比:
Picasso:compile 'com.squareup.picasso:picasso:2.5.1'
Glide:compile 'com.github.bumptech.glide:glide:3.7.0'
Glide默认加载图片是RGB_565的,而Picasso默认加载的图片是ARGB_8888的,会比Glide更占用内存,但是图片质量要高
我们可以创建一个实现了GlideModule的类,将图片格式保存为ARGB_8888的,具体如下:
/** * Created by ping.Zh on 2018/4/11. */ public class GlideConfiguration implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); } @Override public void registerComponents(Context context, Glide glide) { } }
同时在manifest中将Module定义为meta-data:
android:name="com.xxx.xxx.GlideConfiguration"
android:value="GlideModule"/>
Glide的优点是可以计算出任意尺寸的imageview,并将bitmap缓存到磁盘中。而Picasso是缓存的全尺寸的。Glide会将每种大小的的图片都缓存一次,尽管一张图片已经缓存了一次,但是假如你要在另外一个地方再次以不同尺寸显示,需要重新下载,调整成新尺寸的大小,然后将这个尺寸的也缓存起来。
不过我们可以改变这种策略,让Glide既缓存全尺寸又缓存其他尺寸的
Glide.with(this) .load(imageUrl) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageView);
那么下次再要加载别的尺寸的图片的时候,就会从缓存中取出全尺寸的图片,调整大小加载到imageview上并重新缓存,通过这种操作,可以使Glide加载非常快。
Glide还有一个优势就是可以加载动图GIF,同时因为Glide和Activity/Fragment的生命周期是一致的,因此gif的动画也会自动的随着Activity/Fragment的状态暂停、重放。Glide 的缓存在gif这里也是一样,调整大小然后缓存。
Glide的缺点是库比Picasso稍微大点,但是方法数比Picasso多很多,因此在使用Glide的时候必须开启混淆
Glide的其他配置可参考:https://github.com/bumptech/glide/wiki/Configuration
Glide.with(this) .load(imageUrl) .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageView);
重点依据这个线路来分析源码:
Glide的with()方法有很多重载的方法
/** * Begin a load with Glide by passing in a context. * ** Any requests started using a context will only have the application level options applied and will not be * started or stopped based on lifecycle events. In general, loads should be started at the level the result * will be used in. If the resource will be used in a view in a child fragment, * the load should be started with {@link #with(android.app.Fragment)}} using that child fragment. Similarly, * if the resource will be used in a view in the parent fragment, the load should be started with * {@link #with(android.app.Fragment)} using the parent fragment. In the same vein, if the resource will be used * in a view in an activity, the load should be started with {@link #with(android.app.Activity)}}. * * *
* This method is appropriate for resources that will be used outside of the normal fragment or activity * lifecycle (For example in services, or for notification thumbnails). * * * @see #with(android.app.Activity) * @see #with(android.app.Fragment) * @see #with(android.support.v4.app.Fragment) * @see #with(android.support.v4.app.FragmentActivity) * * @param context Any context, will not be retained. * @return A RequestManager for the top level application that can be used to start a load. */ public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); } /** * Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle and that uses the * given {@link Activity}'s default options. * * @param activity The activity to use. * @return A RequestManager for the given activity that can be used to start a load. */ public static RequestManager with(Activity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); } /** * Begin a load with Glide that will tied to the give {@link android.support.v4.app.FragmentActivity}'s lifecycle * and that uses the given {@link android.support.v4.app.FragmentActivity}'s default options. * * @param activity The activity to use. * @return A RequestManager for the given FragmentActivity that can be used to start a load. */ public static RequestManager with(FragmentActivity activity) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity); } /** * Begin a load with Glide that will be tied to the given {@link android.app.Fragment}'s lifecycle and that uses * the given {@link android.app.Fragment}'s default options. * * @param fragment The fragment to use. * @return A RequestManager for the given Fragment that can be used to start a load. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public static RequestManager with(android.app.Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); } /** * Begin a load with Glide that will be tied to the given {@link android.support.v4.app.Fragment}'s lifecycle and * that uses the given {@link android.support.v4.app.Fragment}'s default options. * * @param fragment The fragment to use. * @return A RequestManager for the given Fragment that can be used to start a load. */ public static RequestManager with(Fragment fragment) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment); }
从上面的重载方法中可以看到,Glide的with中既可以传入context,也可以传入activity和fragment。每一个with方法都很简单,都是先调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,没什么需要解释的。然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。
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); }
发现get方法会将传入的context参数根据是否是application来分别分别加载,那么接下来肯定有很多get的重载方法
public RequestManager get(FragmentActivity activity) { if (Util.isOnBackgroundThread()) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); FragmentManager fm = activity.getSupportFragmentManager(); return supportFragmentGet(activity, fm); } } public RequestManager get(Fragment fragment) { if (fragment.getActivity() == null) { throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); } if (Util.isOnBackgroundThread()) { return get(fragment.getActivity().getApplicationContext()); } else { FragmentManager fm = fragment.getChildFragmentManager(); return supportFragmentGet(fragment.getActivity(), fm); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm); } }
private RequestManager getApplicationManager(Context context) { // Either an application context or we're on a background thread. if (applicationManager == null) { synchronized (this) { if (applicationManager == null) { // Normally pause/resume is taken care of by the fragment we add to the fragment or activity. // However, in this case since the manager attached to the application will not receive lifecycle // events, we must force the manager to start resumed using ApplicationLifecycle. applicationManager = new RequestManager(context.getApplicationContext(), new ApplicationLifecycle(), new EmptyRequestManagerTreeNode()); } } } return applicationManager; }我们先来看传入Application参数的情况。如果在Glide.with()方法中传入的是一个Application对象,那么这里就会调用带有Context参数的get()方法重载,然后会在第44行调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
接下来我们看传入非Application参数的情况。不管你在Glide.with()方法中传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。
那么这里为什么要添加一个隐藏的Fragment呢?因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
这里额外再提一句,从第48行代码可以看出,如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。总体来说,第一个with()方法的源码还是比较好理解的。其实就是为了得到一个RequestManager对象而已,然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。
/** * 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 DrawableTypeRequestload(String string) { return (DrawableTypeRequest ) fromString().load(string); }
由于with()方法返回的是一个RequestManager对象,那么很容易就能想到,load()方法是在RequestManager类当中的,所以说我们首先要看的就是RequestManager这个类。load()方法肯定也是有很多重载的,我们只看load url这个的
/** * 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 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. * * * @see #from(Class) * @see #load(String) */ public DrawableTypeRequest
fromString() { return loadGeneric(String.class); }
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) { ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context); ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context); if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) { throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for" + " which there is a registered ModelLoader, if you are using a custom model, you must first call" + " Glide#register with a ModelLoaderFactory for your custom model class"); } return optionsApplier.apply( new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier)); }这个方法中的逻辑是非常简单的,只有一行代码,就是先调用了fromString()方法,再调用load()方法,然后把传入的图片URL地址传进去。而fromString()方法也极为简单,就是调用了loadGeneric()方法,并且指定参数为String.class,因为load()方法传入的是一个字符串参数。那么看上去,好像主要的工作都是在loadGeneric()方法中进行的了。
其实loadGeneric()方法也没几行代码,这里分别调用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象。ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象。不过buildStreamModelLoader()方法内部的逻辑还是蛮复杂的,这里就不展开介绍了。由于我们刚才传入的参数是String.class,因此最终得到的是StreamStringLoader对象,它是实现了ModelLoader接口的。
最后我们可以看到,loadGeneric()方法是要返回一个DrawableTypeRequest对象的,因此在loadGeneric()方法的最后又去new了一个DrawableTypeRequest对象,然后把刚才获得的ModelLoader对象,还有一大堆杂七杂八的东西都传了进去。具体每个参数的含义和作用就不解释了,我们只看主线流程。
那么DrawableTypeRequest是做什么的呢?接着看
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions { private final ModelLoader<ModelType, InputStream> streamModelLoader; private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader; private final RequestManager.OptionsApplier optionsApplier; private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide, ModelLoader<A, InputStream> streamModelLoader, ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass, Class<R> transcodedClass, ResourceTranscoder<Z, R> transcoder) { if (streamModelLoader == null && fileDescriptorModelLoader == null) { return null; } if (transcoder == null) { transcoder = glide.buildTranscoder(resourceClass, transcodedClass); } DataLoadProvider这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了asBitmap()和asGif()这两个方法。, 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}. * * 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. * * * @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)); }
好的,那么我们再回到RequestManager的load()方法中。刚才已经分析过了,fromString()方法会返回一个DrawableTypeRequest对象,接下来会调用这个对象的load()方法,把图片的URL地址传进去。但是我们刚才看到了,DrawableTypeRequest中并没有load()方法,那么很容易就能猜想到,load()方法是在父类当中的。
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) * ** 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)}. * * *
* Only the thumbnail call on the main request will be obeyed, recursive calls to this method are ignored. * * * @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
, GifBitmapWrapper> decoder) { super.decoder(decoder); return this; } /** * {@inheritDoc} */ @Override public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder , GifBitmapWrapper> cacheDecoder) { super.cacheDecoder(cacheDecoder); return this; } /** * {@inheritDoc} */ @Override public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder 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. * * * Note - Bitmap transformations will apply individually to each frame of animated GIF images and also to * individual {@link Bitmap}s. * * * @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
... 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 ... transformation) { super.transform(transformation); return this; } /** * {@inheritDoc} */ @Override public DrawableRequestBuilder<ModelType> transcoder( ResourceTranscoder , GlideDrawable> transcoder) { super.transcoder(transcoder); return this; } /** * {@inheritDoc} */ public final DrawableRequestBuilder<ModelType> crossFade() { super.animate(new DrawableCrossFadeFactory ()); return this; } /** * {@inheritDoc} */ public DrawableRequestBuilder<ModelType> crossFade(int duration) { super.animate(new DrawableCrossFadeFactory (duration)); return this; } /** * {@inheritDoc} */ @Deprecated public DrawableRequestBuilder<ModelType> crossFade(Animation animation, int duration) { super.animate(new DrawableCrossFadeFactory (animation, duration)); return this; } /** * {@inheritDoc} */ public DrawableRequestBuilder<ModelType> crossFade(int animationId, int duration) { super.animate(new DrawableCrossFadeFactory (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; } /** * {@inheritDoc} */ @Override public DrawableRequestBuilder<ModelType> sourceEncoder(Encoder sourceEncoder) { super.sourceEncoder(sourceEncoder); return this; } /** * {@inheritDoc} */ @Override public DrawableRequestBuilder<ModelType> dontTransform() { super.dontTransform(); return this; } @Override public DrawableRequestBuilder<ModelType> signature(Key signature) { super.signature(signature); return this; } @Override public DrawableRequestBuilder<ModelType> load(ModelType model) { super.load(model); return this; } @Override public DrawableRequestBuilder<ModelType> clone() { return (DrawableRequestBuilder<ModelType>) super.clone(); } /** * {@inheritDoc} * * * 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()}. * * * @param view {@inheritDoc} * @return {@inheritDoc} */ @Override public Target
into(ImageView view) { return super.into(view); } @Override void applyFitCenter() { fitCenter(); } @Override void applyCenterCrop() { centerCrop(); } }
这个类中绝大多数就是Glide的API方法了,最终load()方法返回的其实就是一个DrawableTypeRequest对象。
@Override public Targetinto(ImageView view) { return super.into(view); }
从刚才的代码来看,into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。
DrawableRequestBuilder的父类是GenericRequestBuilder,我们来看一下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)); }
这里发现一个小小的插曲,
/** * Throws an {@link java.lang.IllegalArgumentException} if called on a thread other than the main thread. */ public static void assertMainThread() { if (!isOnMainThread()) { throw new IllegalArgumentException("You must call this method on the main thread"); } }
我还以为assertMainThread是怎么切换到主线程的,原来只是做了个判断,并抛出一个异常!!!
最后一行代码先是调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) { return imageViewTargetFactory.buildTarget(imageView, transcodedClass); }
/** * A factory responsible for producing the correct type of {@link com.bumptech.glide.request.target.Target} for a given * {@link android.view.View} subclass. */ public class ImageViewTargetFactory { @SuppressWarnings("unchecked") public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) { if (GlideDrawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new GlideDrawableImageViewTarget(view); } else if (Bitmap.class.equals(clazz)) { return (Target<Z>) new BitmapImageViewTarget(view); } else if (Drawable.class.isAssignableFrom(clazz)) { return (Target<Z>) new DrawableImageViewTarget(view); } else { throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)"); } } }
可以看到,在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。那如果你要分析这个class参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳理清楚。这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。
也就是说,通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。我们来看一下这个into()方法的源码:
/** * 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"); } if (!isModelSet) { throw new IllegalArgumentException("You must first set a model (try #load())"); } Request previous = target.getRequest(); if (previous != null) { previous.clear(); requestTracker.removeRequest(previous); previous.recycle(); } Request request = buildRequest(target); target.setRequest(request); lifecycle.addListener(target); requestTracker.runRequest(request); return target; }
这里我们还是只抓核心代码,其实只有两行是最关键的,调用buildRequest()方法构建出了一个Request对象,还有执行这个Request。
Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
private Request buildRequest(Target<TranscodeType> target) { if (priority == null) { priority = Priority.NORMAL; } return buildRequestRecursive(target, null); } private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) { if (thumbnailRequestBuilder != null) { if (isThumbnailBuilt) { throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, " + "consider using clone() on the request(s) passed to thumbnail()"); } // Recursive case: contains a potentially recursive thumbnail request builder. if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) { thumbnailRequestBuilder.animationFactory = animationFactory; } if (thumbnailRequestBuilder.priority == null) { thumbnailRequestBuilder.priority = getThumbnailPriority(); } if (Util.isValidDimensions(overrideWidth, overrideHeight) && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth, thumbnailRequestBuilder.overrideHeight)) { thumbnailRequestBuilder.override(overrideWidth, overrideHeight); } ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); // Guard against infinite recursion. isThumbnailBuilt = true; // Recursively generate thumbnail requests. Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse. ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator); Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator); Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // Base case: no thumbnail. return obtainRequest(target, sizeMultiplier, priority, parentCoordinator); } } private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority, RequestCoordinator requestCoordinator) { return GenericRequest.obtain( loadProvider, model, signature, context, priority, target, sizeMultiplier, placeholderDrawable, placeholderId, errorPlaceholder, errorId, fallbackDrawable, fallbackResource, requestListener, requestCoordinator, glide.getEngine(), transformation, transcodeClass, isCacheable, animationFactory, overrideWidth, overrideHeight, diskCacheStrategy); }
可以看到,buildRequest()方法的内部其实又调用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代码虽然有点长,但是其中90%的代码都是在处理缩略图的。
这里调用了obtainRequest()方法来获取一个Request对象,而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。
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>(); } 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; }这里在先去new了一个GenericRequest对象,并在最后一行返回,也就是说,obtain()方法实际上获得的就是一个GenericRequest对象。另外这里又调用了GenericRequest的init(),里面主要就是一些赋值的代码,将传入的这些参数赋值到GenericRequest的成员变量当中,我们就不再跟进去看了。
好,那现在解决了构建Request对象的问题,接下来我们看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示:
/** * Starts tracking the given request. */ public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
这里有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
我们重点来看这个begin()方法。由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:
/** * {@inheritDoc} */ @Override public void begin() { startTime = LogTime.getLogTime(); if (model == null) { onException(null); return; } status = Status.WAITING_FOR_SIZE; if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); } if (!isComplete() && !isFailed() && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }这里我们来注意几个细节,首先如果model等于null,model也就是我们在第二步load()方法中传入的图片URL地址,这个时候会调用onException()方法。而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。
好,那么我们继续回到begin()方法。刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。如果没指定的话,就会执行第12行代码,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。最后也还会调用onSizeReady方法。
/** * A callback method that should never be invoked directly. */ @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; 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)); } }这里将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来我们就要看一看,这个Engine的load()方法当中,到底做了什么?代码如下所示
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(); 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 = 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); }
load()方法中的代码虽然有点长,但大多数的代码都是在处理缓存的。关于缓存的内容我们会在下一篇文章当中学习,现在只需要从第45行看起就行。这里构建了一个EngineJob,它的主要作用就是用来开启线程的,为后面的异步加载图片做准备。接下来第46行创建了一个DecodeJob对象,从名字上来看,它好像是用来对图片进行解码的,但实际上它的任务十分繁重,待会我们就知道了。继续往下看,第48行创建了一个EngineRunnable对象,并且在51行调用了EngineJob的start()方法来运行EngineRunnable对象,这实际上就是让EngineRunnable的run()方法在子线程当中执行了。那么我们现在就可以去看看EngineRunnable的run()方法里做了些什么,如下所示:
@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); } }
这里调用了一个decode()方法,并且这个方法返回了一个Resource对象。看上去所有的逻辑应该都在这个decode()方法执行的了,那我们跟进去瞧一瞧
private Resource> decode() throws Exception { if (isDecodingFromCache()) { return decodeFromCache(); } else { return decodeFromSource(); } }
private Resource> decodeFromSource() throws Exception { return 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(); final A data = fetcher.loadData(priority); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Fetched data", startTime); } if (isCancelled) { return null; } decoded = decodeFromSourceData(data); } finally { fetcher.cleanup(); } return decoded; }
主要的方法就这些,我都帮大家提取出来了。那么我们先来看一下decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()方法来处理这个Resource对象。
那么我们先来看第一步,decodeSource()方法中的逻辑也并不复杂,首先在调用了fetcher.loadData()方法。那么这个fetcher是什么呢?其实就是刚才在onSizeReady()方法中得到的ImageVideoFetcher对象,这里调用它的loadData()方法,代码如下所示:
@SuppressWarnings("resource") // @see ModelLoader.loadData @Override public ImageVideoWrapper loadData(Priority priority) throws Exception { InputStream is = null; if (streamFetcher != null) { try { 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; } } } return new ImageVideoWrapper(is, fileDescriptor); }
@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> 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> 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()); } }
经过一层一层地跋山涉水,我们终于在这里找到网络通讯的代码了!
loadData()方法只是返回了一个InputStream,服务器返回的数据连读都还没开始读呢。所以我们还是要静下心来继续分析,回到刚才ImageVideoFetcher的loadData()方法中,在这个方法的最后一行,创建了一个ImageVideoWrapper对象,并把刚才得到的InputStream作为参数传了进去。
然后我们回到再上一层,也就是DecodeJob的decodeSource()方法当中,在得到了这个ImageVideoWrapper对象之后,紧接着又将这个对象传入到了decodeFromSourceData()当中,来去解码这个对象。decodeFromSourceData()方法的代码如下所示:
private Resource<T> decodeFromSourceData(A data) throws IOException { final Resource<T> decoded; if (diskCacheStrategy.cacheSource()) { decoded = cacheAndDecodeSourceData(data); } else { long startTime = LogTime.getLogTime(); decoded = loadProvider.getSourceDecoder().decode(data, width, height); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Decoded from source", startTime); } } return decoded; }可以看到,这里在第7行调用了loadProvider.getSourceDecoder().decode()方法来进行解码。loadProvider就是刚才在onSizeReady()方法中得到的FixedLoadProvider,而getSourceDecoder()得到的则是一个GifBitmapWrapperResourceDecoder对象,也就是要调用这个对象的decode()方法来对图片进行解码。