美文网首页Android基础
Android图片加载框架最全解析(二)中,从源码的角度理解G

Android图片加载框架最全解析(二)中,从源码的角度理解G

作者: 社会我鹏哥 | 来源:发表于2017-05-27 20:54 被阅读172次

    特此声明:本文为转载文章!尊重原创的劳动果实,严禁剽窃
    本文转载于: http://blog.csdn.net/guolin_blog/article/details/53939176
    出自于:【郭霖的博客】

    3. into()

    如果说前面两步都是在准备开胃小菜的话,那么现在终于要进入主菜了,因为into()方法也是整个Glide图片加载流程中逻辑最复杂的地方。

    不过从刚才的代码来看,into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。

    DrawableRequestBuilder的父类是GenericRequestBuilder,我们来看一下GenericRequestBuilder类中的into()方法,如下所示:

    public Target<TranscodeType> into(ImageView view) {
        Util.assertMainThread();
        if (view == null) {
            throw new IllegalArgumentException("You must pass in a non null View");
        }
        if (!isTransformationSet && view.getScaleType() != null) {
            switch (view.getScaleType()) {
                case CENTER_CROP:
                    applyCenterCrop();
                    break;
                case FIT_CENTER:
                case FIT_START:
                case FIT_END:
                    applyFitCenter();
                    break;
                //$CASES-OMITTED$
                default:
                    // Do nothing.
            }
        }
        return into(glide.buildImageViewTarget(view, transcodeClass));
    }
    

    这里前面一大堆的判断逻辑我们都可以先不用管,等到后面文章讲transform的时候会再进行解释,现在我们只需要关注最后一行代码。最后一行代码先是调用了glide.buildImageViewTarget()方法,这个方法会构建出一个Target对象,Target对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码:

    <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
        return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
    }
    

    这里其实又是调用了ImageViewTargetFactory的buildTarget()方法,我们继续跟进去,代码如下所示:

    public class ImageViewTargetFactory {
    
        @SuppressWarnings("unchecked")
        public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
            if (GlideDrawable.class.isAssignableFrom(clazz)) {
                return (Target<Z>) new GlideDrawableImageViewTarget(view);
            } else if (Bitmap.class.equals(clazz)) {
                return (Target<Z>) new BitmapImageViewTarget(view);
            } else if (Drawable.class.isAssignableFrom(clazz)) {
                return (Target<Z>) new DrawableImageViewTarget(view);
            } else {
                throw new IllegalArgumentException("Unhandled class: " + clazz
                        + ", try .as*(Class).transcode(ResourceTranscoder)");
            }
        }
    }
    

    可以看到,在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。那如果你要分析这个class参数是从哪儿传过来的,这可有得你分析了,简单起见我直接帮大家梳理清楚。这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,否则的话构建的都是GlideDrawableImageViewTarget对象。至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。

    也就是说,通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。我们来看一下这个into()方法的源码:

    public <Y extends Target<TranscodeType>> Y into(Y target) {
        Util.assertMainThread();
        if (target == null) {
            throw new IllegalArgumentException("You must pass in a non null Target");
        }
        if (!isModelSet) {
            throw new IllegalArgumentException("You must first set a model (try #load())");
        }
        Request previous = target.getRequest();
        if (previous != null) {
            previous.clear();
            requestTracker.removeRequest(previous);
            previous.recycle();
        }
        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        requestTracker.runRequest(request);
        return target;
    }
    

    这里我们还是只抓核心代码,其实只有两行是最关键的,第15行调用buildRequest()方法构建出了一个Request对象,还有第18行来执行这个Request。

    Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:

    private Request buildRequest(Target<TranscodeType> target) {
        if (priority == null) {
            priority = Priority.NORMAL;
        }
        return buildRequestRecursive(target, null);
    }
    
    private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
        if (thumbnailRequestBuilder != null) {
            if (isThumbnailBuilt) {
                throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
                        + "consider using clone() on the request(s) passed to thumbnail()");
            }
            // Recursive case: contains a potentially recursive thumbnail request builder.
            if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
                thumbnailRequestBuilder.animationFactory = animationFactory;
            }
    
            if (thumbnailRequestBuilder.priority == null) {
                thumbnailRequestBuilder.priority = getThumbnailPriority();
            }
    
            if (Util.isValidDimensions(overrideWidth, overrideHeight)
                    && !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
                            thumbnailRequestBuilder.overrideHeight)) {
              thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
            }
    
            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
            // Guard against infinite recursion.
            isThumbnailBuilt = true;
            // Recursively generate thumbnail requests.
            Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
            isThumbnailBuilt = false;
            coordinator.setRequests(fullRequest, thumbRequest);
            return coordinator;
        } else if (thumbSizeMultiplier != null) {
            // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
            Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
            coordinator.setRequests(fullRequest, thumbnailRequest);
            return coordinator;
        } else {
            // Base case: no thumbnail.
            return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
        }
    }
    
    private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
            RequestCoordinator requestCoordinator) {
        return GenericRequest.obtain(
                loadProvider,
                model,
                signature,
                context,
                priority,
                target,
                sizeMultiplier,
                placeholderDrawable,
                placeholderId,
                errorPlaceholder,
                errorId,
                fallbackDrawable,
                fallbackResource,
                requestListener,
                requestCoordinator,
                glide.getEngine(),
                transformation,
                transcodeClass,
                isCacheable,
                animationFactory,
                overrideWidth,
                overrideHeight,
                diskCacheStrategy);
    }
    

    可以看到,buildRequest()方法的内部其实又调用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代码虽然有点长,但是其中90%的代码都是在处理缩略图的。如果我们只追主线流程的话,那么只需要看第47行代码就可以了。这里调用了obtainRequest()方法来获取一个Request对象,而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。那么我们进入到这个GenericRequest的obtain()方法瞧一瞧:

    public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
            ResourceCallback {
    
        ...
    
        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;
        }
    
        ...
    }
    

    可以看到,这里在第33行去new了一个GenericRequest对象,并在最后一行返回,也就是说,obtain()方法实际上获得的就是一个GenericRequest对象。另外这里又在第35行调用了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()方法了,如下所示:

    @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()方法。如果你跟到onException()方法里面去看看,你会发现它最终会调用到一个setErrorPlaceholder()当中,如下所示:

    private void setErrorPlaceholder(Exception e) {
        if (!canNotifyStatusChanged()) {
            return;
        }
        Drawable error = model == null ? getFallbackDrawable() : null;
        if (error == null) {
          error = getErrorDrawable();
        }
        if (error == null) {
            error = getPlaceholderDrawable();
        }
        target.onLoadFailed(e, error);
    }
    

    这个方法中会先去获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用target.onLoadFailed()方法并将占位图传入。那么onLoadFailed()方法中做了什么呢?我们看一下:

    public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
    
        ...
    
        @Override
        public void onLoadStarted(Drawable placeholder) {
            view.setImageDrawable(placeholder);
        }
    
        @Override
        public void onLoadFailed(Exception e, Drawable errorDrawable) {
            view.setImageDrawable(errorDrawable);
        }
    
        ...
    }
    

    很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的placeholder()和error()这两个占位图API底层的实现原理。

    好,那么我们继续回到begin()方法。刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。如果没指定的话,就会执行第12行代码,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用onSizeReady()方法。也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,在这里进行下一步操作。那么我们跟到这个方法里面来:

    @Override
    public void onSizeReady(int width, int height) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
        }
        if (status != Status.WAITING_FOR_SIZE) {
            return;
        }
        status = Status.RUNNING;
        width = Math.round(sizeMultiplier * width);
        height = Math.round(sizeMultiplier * height);
        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));
        }
    }
    

    从这里开始,真正复杂的地方来了,我们需要慢慢进行分析。先来看一下,在第12行调用了loadProvider.getModelLoader()方法,那么我们第一个要搞清楚的就是,这个loadProvider是什么?要搞清楚这点,需要先回到第二步的load()方法当中。还记得load()方法是返回一个DrawableTypeRequest对象吗?刚才我们只是分析了DrawableTypeRequest当中的asBitmap()和asGif()方法,并没有仔细看它的构造函数,现在我们重新来看一下DrawableTypeRequest类的构造函数:

    public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
    
        private final ModelLoader<ModelType, InputStream> streamModelLoader;
        private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
        private final RequestManager.OptionsApplier optionsApplier;
    
        private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
                ModelLoader<A, InputStream> streamModelLoader,
                ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
                Class<R> transcodedClass,
                ResourceTranscoder<Z, R> transcoder) {
            if (streamModelLoader == null && fileDescriptorModelLoader == null) {
                return null;
            }
            if (transcoder == null) {
                transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
            }
            DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
                    resourceClass);
            ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
                    fileDescriptorModelLoader);
            return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
        }
    
        DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
                ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
                RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
            super(context, modelClass,
                    buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
                            GlideDrawable.class, null),
                    glide, requestTracker, lifecycle);
            this.streamModelLoader = streamModelLoader;
            this.fileDescriptorModelLoader = fileDescriptorModelLoader;
            this.optionsApplier = optionsApplier;
        }
    
        ...
    }
    

    可以看到,这里在第29行,也就是构造函数中,调用了一个buildProvider()方法,并把streamModelLoader和fileDescriptorModelLoader等参数传入到这个方法中,这两个ModelLoader就是之前在loadGeneric()方法中构建出来的。

    那么我们再来看一下buildProvider()方法里面做了什么,在第16行调用了glide.buildTranscoder()方法来构建一个ResourceTranscoder,它是用于对图片进行转码的,由于ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象。

    接下来在第18行调用了glide.buildDataProvider()方法来构建一个DataLoadProvider,它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。

    然后在第20行,new了一个ImageVideoModelLoader的实例,并把之前loadGeneric()方法中构建的两个ModelLoader封装到了ImageVideoModelLoader当中。

    最后,在第22行,new出一个FixedLoadProvider,并把刚才构建的出来的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。

    好的,那么我们回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行,分别调用了loadProvider的getModelLoader()方法和getTranscoder()方法,那么得到的对象也就是刚才我们分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。而在第13行,又调用了ImageVideoModelLoader的getResourceFetcher()方法,这里我们又需要跟进去瞧一瞧了,代码如下所示:

    public class ImageVideoModelLoader<A> implements ModelLoader<A, ImageVideoWrapper> {
        private static final String TAG = "IVML";
    
        private final ModelLoader<A, InputStream> streamLoader;
        private final ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader;
    
        public ImageVideoModelLoader(ModelLoader<A, InputStream> streamLoader,
                ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader) {
            if (streamLoader == null && fileDescriptorLoader == null) {
                throw new NullPointerException("At least one of streamLoader and fileDescriptorLoader must be non null");
            }
            this.streamLoader = streamLoader;
            this.fileDescriptorLoader = fileDescriptorLoader;
        }
    
        @Override
        public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
            DataFetcher<InputStream> streamFetcher = null;
            if (streamLoader != null) {
                streamFetcher = streamLoader.getResourceFetcher(model, width, height);
            }
            DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
            if (fileDescriptorLoader != null) {
                fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
            }
    
            if (streamFetcher != null || fileDescriptorFetcher != null) {
                return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
            } else {
                return null;
            }
        }
    
        static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
            private final DataFetcher<InputStream> streamFetcher;
            private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;
    
            public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
                    DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
                this.streamFetcher = streamFetcher;
                this.fileDescriptorFetcher = fileDescriptorFetcher;
            }
    
            ...
        }
    }
    

    可以看到,在第20行会先调用streamLoader.getResourceFetcher()方法获取一个DataFetcher,而这个streamLoader其实就是我们在loadGeneric()方法中构建出的StreamStringLoader,调用它的getResourceFetcher()方法会得到一个HttpUrlFetcher对象。然后在第28行new出了一个ImageVideoFetcher对象,并把获得的HttpUrlFetcher对象传进去。也就是说,ImageVideoModelLoader的getResourceFetcher()方法得到的是一个ImageVideoFetcher。

    那么我们再次回到onSizeReady()方法,在onSizeReady()方法的第23行,这里将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来我们就要看一看,这个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) {
            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);
        }
    }
    

    这个方法中的代码并不多,但我们仍然还是要抓重点。在第9行,这里调用了一个decode()方法,并且这个方法返回了一个Resource对象。看上去所有的逻辑应该都在这个decode()方法执行的了,那我们跟进去瞧一瞧:

    private Resource<?> decode() throws Exception {
        if (isDecodingFromCache()) {
            return decodeFromCache();
        } else {
            return decodeFromSource();
        }
    }
    

    decode()方法中又分了两种情况,从缓存当中去decode图片的话就会执行decodeFromCache(),否则的话就执行decodeFromSource()。本篇文章中我们不讨论缓存的情况,那么就直接来看decodeFromSource()方法的代码吧,如下所示:

    private Resource<?> decodeFromSource() throws Exception {
        return decodeJob.decodeFromSource();
    }
    

    这里又调用了DecodeJob的decodeFromSource()方法。刚才已经说了,DecodeJob的任务十分繁重,我们继续跟进看一看吧:

    class DecodeJob<A, T, Z> {
    
        ...
    
        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()方法中的逻辑也并不复杂,首先在第14行调用了fetcher.loadData()方法。那么这个fetcher是什么呢?其实就是刚才在onSizeReady()方法中得到的ImageVideoFetcher对象,这里调用它的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);
    }
    

    可以看到,在ImageVideoFetcher的loadData()方法的第6行,这里又去调用了streamFetcher.loadData()方法,那么这个streamFetcher是什么呢?自然就是刚才在组装ImageVideoFetcher对象时传进来的HttpUrlFetcher了。因此这里又会去调用HttpUrlFetcher的loadData()方法,那么我们继续跟进去瞧一瞧:

    public class HttpUrlFetcher implements DataFetcher<InputStream> {
    
        ...
    
        @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());
            }
        }
    
        private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
                throws IOException {
            if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
                int contentLength = urlConnection.getContentLength();
                stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
            } else {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
                }
                stream = urlConnection.getInputStream();
            }
            return stream;
        }
    
        ...
    }
    

    经过一层一层地跋山涉水,我们终于在这里找到网络通讯的代码了!之前有朋友跟我讲过,说Glide的源码实在是太复杂了,甚至连网络请求是在哪里发出去的都找不到。我们也是经过一段一段又一段的代码跟踪,终于把网络请求的代码给找出来了,实在是太不容易了。

    不过也别高兴得太早,现在离最终分析完还早着呢。可以看到,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()方法来对图片进行解码。那么我们来看下GifBitmapWrapperResourceDecoder的代码:

    public class GifBitmapWrapperResourceDecoder implements ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> {
    
        ...
    
        @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);
            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);
            }
            return result;
        }
    
        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) {
                result = new GifBitmapWrapper(bitmapResource, null);
            }
            return result;
        }
    
        ...
    }
    

    首先,在decode()方法中,又去调用了另外一个decode()方法的重载。然后在第23行调用了decodeStream()方法,准备从服务器返回的流当中读取数据。decodeStream()方法中会先从流中读取2个字节的数据,来判断这张图是GIF图还是普通的静图,如果是GIF图就调用decodeGifWrapper()方法来进行解码,如果是普通的静图就用调用decodeBitmapWrapper()方法来进行解码。这里我们只分析普通静图的实现流程,GIF图的实现有点过于复杂了,无法在本篇文章当中分析。

    然后我们来看一下decodeBitmapWrapper()方法,这里在第52行调用了bitmapDecoder.decode()方法。这个bitmapDecoder是一个ImageVideoBitmapDecoder对象,那么我们来看一下它的代码,如下所示:

    public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> {
        private final ResourceDecoder<InputStream, Bitmap> streamDecoder;
        private final ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder;
    
        public ImageVideoBitmapDecoder(ResourceDecoder<InputStream, Bitmap> streamDecoder,
                ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder) {
            this.streamDecoder = streamDecoder;
            this.fileDescriptorDecoder = fileDescriptorDecoder;
        }
    
        @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 {
                    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;
        }
    
        ...
    }
    

    代码并不复杂,在第14行先调用了source.getStream()来获取到服务器返回的InputStream,然后在第17行调用streamDecoder.decode()方法进行解码。streamDecode是一个StreamBitmapDecoder对象,那么我们再来看这个类的源码,如下所示:

    public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
    
        ...
    
        private final Downsampler downsampler;
        private BitmapPool bitmapPool;
        private DecodeFormat decodeFormat;
    
        public StreamBitmapDecoder(Downsampler downsampler, BitmapPool bitmapPool, DecodeFormat decodeFormat) {
            this.downsampler = downsampler;
            this.bitmapPool = bitmapPool;
            this.decodeFormat = decodeFormat;
        }
    
        @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);
        }
    
        ...
    }
    

    可以看到,它的decode()方法又去调用了Downsampler的decode()方法。接下来又到了激动人心的时刻了,Downsampler的代码如下所示:

    public abstract class Downsampler implements BitmapDecoder<InputStream> {
    
        ...
    
        @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);
            }
        }
    
        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);
        }
    
        /**
         * A method for getting the dimensions of an image from the given InputStream.
         *
         * @param is The InputStream representing the image.
         * @param options The options to pass to
         *          {@link BitmapFactory#decodeStream(InputStream, android.graphics.Rect,
         *              BitmapFactory.Options)}.
         * @return an array containing the dimensions of the image in the form {width, height}.
         */
        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 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;
        }
    
        ...
    }
    

    可以看到,对服务器返回的InputStream的读取,以及对图片的加载全都在这里了。当然这里其实处理了很多的逻辑,包括对图片的压缩,甚至还有旋转、圆角等逻辑处理,但是我们目前只需要关注主线逻辑就行了。decode()方法执行之后,会返回一个Bitmap对象,那么图片在这里其实也就已经被加载出来了,剩下的工作就是如果让这个Bitmap显示到界面上,我们继续往下分析。

    回到刚才的StreamBitmapDecoder当中,你会发现,它的decode()方法返回的是一个Resource<Bitmap>对象。而我们从Downsampler中得到的是一个Bitmap对象,因此这里在第18行又调用了BitmapResource.obtain()方法,将Bitmap对象包装成了Resource<Bitmap>对象。代码如下所示:

    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 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();
            }
        }
    }
    

    BitmapResource的源码也非常简单,经过这样一层包装之后,如果我还需要获取Bitmap,只需要调用Resource<Bitmap>的get()方法就可以了。

    然后我们需要一层层继续向上返回,StreamBitmapDecoder会将值返回到ImageVideoBitmapDecoder当中,而ImageVideoBitmapDecoder又会将值返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法当中。由于代码隔得有点太远了,我重新把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) {
            result = new GifBitmapWrapper(bitmapResource, null);
        }
        return result;
    }
    

    可以看到,decodeBitmapWrapper()方法返回的是一个GifBitmapWrapper对象。因此,这里在第5行,又将Resource<Bitmap>封装到了一个GifBitmapWrapper对象当中。这个GifBitmapWrapper顾名思义,就是既能封装GIF,又能封装Bitmap,从而保证了不管是什么类型的图片Glide都能从容应对。我们顺便来看下GifBitmapWrapper的源码吧,如下所示:

    public class GifBitmapWrapper {
        private final Resource<GifDrawable> gifResource;
        private final Resource<Bitmap> bitmapResource;
    
        public GifBitmapWrapper(Resource<Bitmap> bitmapResource, Resource<GifDrawable> gifResource) {
            if (bitmapResource != null && gifResource != null) {
                throw new IllegalArgumentException("Can only contain either a bitmap resource or a gif resource, not both");
            }
            if (bitmapResource == null && gifResource == null) {
                throw new IllegalArgumentException("Must contain either a bitmap resource or a gif resource");
            }
            this.bitmapResource = bitmapResource;
            this.gifResource = gifResource;
        }
    
        /**
         * Returns the size of the wrapped resource.
         */
        public int getSize() {
            if (bitmapResource != null) {
                return bitmapResource.getSize();
            } else {
                return gifResource.getSize();
            }
        }
    
        /**
         * Returns the wrapped {@link Bitmap} resource if it exists, or null.
         */
        public Resource<Bitmap> getBitmapResource() {
            return bitmapResource;
        }
    
        /**
         * Returns the wrapped {@link GifDrawable} resource if it exists, or null.
         */
        public Resource<GifDrawable> getGifResource() {
            return gifResource;
        }
    }
    

    还是比较简单的,就是分别对gifResource和bitmapResource做了一层封装而已,相信没有什么解释的必要。

    然后这个GifBitmapWrapper对象会一直向上返回,返回到GifBitmapWrapperResourceDecoder最外层的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;
    }
    

    可以看到,这里在第11行,又将GifBitmapWrapper封装到了一个GifBitmapWrapperResource对象当中,最终返回的是一个Resource<GifBitmapWrapper>对象。这个GifBitmapWrapperResource和刚才的BitmapResource是相似的,它们都实现的Resource接口,都可以通过get()方法来获取封装起来的具体内容。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();
            }
        }
    }
    

    经过这一层的封装之后,我们从网络上得到的图片就能够以Resource接口的形式返回,并且还能同时处理Bitmap图片和GIF图片这两种情况。

    那么现在我们可以回到DecodeJob当中了,它的decodeFromSourceData()方法返回的是一个Resource<T>对象,其实也就是Resource<GifBitmapWrapper>对象了。然后继续向上返回,最终返回到decodeFromSource()方法当中,如下所示:

    public Resource<Z> decodeFromSource() throws Exception {
        Resource<T> decoded = decodeSource();
        return transformEncodeAndTranscode(decoded);
    }
    

    刚才我们就是从这里跟进到decodeSource()方法当中,然后执行了一大堆一大堆的逻辑,最终得到了这个Resource<T>对象。然而你会发现,decodeFromSource()方法最终返回的却是一个Resource<Z>对象,那么这到底是怎么回事呢?我们就需要跟进到transformEncodeAndTranscode()方法来瞧一瞧了,代码如下所示:

    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<Z> result = transcode(transformed);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transcoded transformed from source", startTime);
        }
        return result;
    }
    
    private Resource<Z> transcode(Resource<T> transformed) {
        if (transformed == null) {
            return null;
        }
        return transcoder.transcode(transformed);
    }
    

    首先,这个方法开头的几行transform还有cache,这都是我们后面才会学习的东西,现在不用管它们就可以了。需要注意的是第9行,这里调用了一个transcode()方法,就把Resource<T>对象转换成Resource<Z>对象了。

    而transcode()方法中又是调用了transcoder的transcode()方法,那么这个transcoder是什么呢?其实这也是Glide源码特别难懂的原因之一,就是它用到的很多对象都是很早很早之前就初始化的,在初始化的时候你可能完全就没有留意过它,因为一时半会根本就用不着,但是真正需要用到的时候你却早就记不起来这个对象是从哪儿来的了。

    那么这里我来提醒一下大家吧,在第二步load()方法返回的那个DrawableTypeRequest对象,它的构建函数中去构建了一个FixedLoadProvider对象,然后我们将三个参数传入到了FixedLoadProvider当中,其中就有一个GifBitmapWrapperDrawableTranscoder对象。后来在onSizeReady()方法中获取到了这个参数,并传递到了Engine当中,然后又由Engine传递到了DecodeJob当中。因此,这里的transcoder其实就是这个GifBitmapWrapperDrawableTranscoder对象。那么我们来看一下它的源码:

    public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> {
        private final ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder;
    
        public GifBitmapWrapperDrawableTranscoder(
                ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder) {
            this.bitmapDrawableResourceTranscoder = bitmapDrawableResourceTranscoder;
        }
    
        @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) {
                result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
            } else {
                result = gifBitmap.getGifResource();
            }
            return (Resource<GlideDrawable>) result;
        }
    
        ...
    }
    

    这里我来简单解释一下,GifBitmapWrapperDrawableTranscoder的核心作用就是用来转码的。因为GifBitmapWrapper是无法直接显示到ImageView上面的,只有Bitmap或者Drawable才能显示到ImageView上。因此,这里的transcode()方法先从Resource<GifBitmapWrapper>中取出GifBitmapWrapper对象,然后再从GifBitmapWrapper中取出Resource<Bitmap>对象。

    相关文章

      网友评论

        本文标题: Android图片加载框架最全解析(二)中,从源码的角度理解G

        本文链接:https://www.haomeiwen.com/subject/drtcfxtx.html