图片加载框架Glide的使用及源码分析

1、对比图片加载框架

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

2、源码分析

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()方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。

loadUrl()方法:

/**
 * 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 load(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, 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)); }
这个类中的代码本身就不多,我只是稍微做了一点简化。可以看到,最主要的就是它提供了asBitmap()和asGif()这两个方法。

好的,那么我们再回到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( RequestListenersuper 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对象。

into()方法

@Override
public Target into(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,
        RequestListenersuper 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()方法来对图片进行解码。





你可能感兴趣的:(面试问题,源码分析,技术总结)