美文网首页Android开发经验谈Android开发
〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(三)

〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(三)

作者: 两行哥 | 来源:发表于2018-04-27 13:36 被阅读55次

    之前我要分析Glide的with()方法以及load()方法的内部逻辑(参阅:〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一)〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(二)),本篇主要分析into()方法。Glide.with().load()方法最终返回值类型为DrawableTypeRequest<ModelType>,而DrawableTypeRequest<ModelType>又继承于DrawableRequestBuilder<ModelType>,DrawableRequestBuilder<ModelType>又继承于GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>,因此本文分析的源码主要集中于这三个类。

    那么问题来了,在Glide的into()方法中依次做了哪些操作呢?
    划重点:
    1.构建Target(什么是Target?请参阅前文)
    2.创建Request,传入之前创建的Target
    3.执行Request,计算图片宽高
    4.加载及解析图片

    一、构建Target

    直接追踪into()方法源码。

    DrawableRequestBuilder.java
        @Override
        public Target<GlideDrawable> into(ImageView view) {
            return super.into(view);
        }
    

    最终返回值为Target。进入父类GenericRequestBuilder继续追踪源码。

    GenericRequestBuilder.java
        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;
                    default:
                        // Do nothing.
                }
            }
            return into(glide.buildImageViewTarget(view, transcodeClass));
        }
    

    因为Android中所有的UI操作皆在主线程,因此方法体内部首先判断into()方法是否在主线程执行(调用Util.assertMainThread()方法)。接下来根据目标view的ScaleType进行图片裁切(Transform。什么是Transform?请参阅前文),主要根据不同情况调用了applyCenterCrop()和applyFitCenter()方法。追踪一下这两个方法的内部逻辑。在GenericRequestBuilder类这两个方法为空实现,因此我们追踪到它的子类BitmapRequestBuilder类中,发现调用的方法为 fitCenter()和centerCrop(),继续追踪这两个方法内的源码,如下文。

    BitmapRequestBuilder.java
        ......省略
        @Override
        void applyFitCenter() {
            fitCenter();
        }
        ......省略
        @Override
        void applyCenterCrop() {
            centerCrop();
        }
        ......省略
        public BitmapRequestBuilder<ModelType, TranscodeType> fitCenter() {
            return transform(glide.getBitmapFitCenter());
        }
        ......省略
        public BitmapRequestBuilder<ModelType, TranscodeType> centerCrop() {
            return transform(glide.getBitmapCenterCrop());
        }
        ......省略
        public BitmapRequestBuilder<ModelType, TranscodeType> transform(BitmapTransformation... transformations) {
            super.transform(transformations);
            return this;
        }
        ......省略
    

    通过一系列的追踪,发现fitCenter()和centerCrop()最终调用了transform(BitmapTransformation... transformations)方法。这里提及一下transform()方法的参数,以上文源码 transform(glide.getBitmapFitCenter())传入的glide.getBitmapFitCenter()为例:
    glide.getBitmapFitCenter()最终返回了Glide类的成员变量bitmapCenterCrop,这是一个CenterCrop的实例。

    Glide.java
        private final CenterCrop bitmapCenterCrop;
        ......省略
        CenterCrop getBitmapCenterCrop() {
            return bitmapCenterCrop;
        }
        ......省略
    

    而CenterCrop类最终实现了Transformation<T>接口,定义了裁切转化(Transform)的逻辑,那么真正执行居中裁切(centerCrop)的逻辑的代码在哪里呢?在CenterCrop类的transform()方法体中我们发现了端倪:

    CenterCrop.java
        protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
            final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
                    ? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
            Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
            if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
                toReuse.recycle();
            }
            return transformed;
        }
    

    注意到Transform()方法内部调用了TransformationUtils.centerCrop()。至此,我们在TransformationUtils类中找到了居中裁切(centerCrop)的真正实现逻辑:

    TransformationUtils.java
        public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
            if (toCrop == null) {
                return null;
            } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {
                return toCrop;
            }
            // From ImageView/Bitmap.createScaledBitmap.
            final float scale;
            float dx = 0, dy = 0;
            Matrix m = new Matrix();
            if (toCrop.getWidth() * height > width * toCrop.getHeight()) {
                scale = (float) height / (float) toCrop.getHeight();
                dx = (width - toCrop.getWidth() * scale) * 0.5f;
            } else {
                scale = (float) width / (float) toCrop.getWidth();
                dy = (height - toCrop.getHeight() * scale) * 0.5f;
            }
    
            m.setScale(scale, scale);
            m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
            final Bitmap result;
            if (recycled != null) {
                result = recycled;
            } else {
                result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
            }
    
            // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
            TransformationUtils.setAlpha(toCrop, result);
    
            Canvas canvas = new Canvas(result);
            Paint paint = new Paint(PAINT_FLAGS);
            canvas.drawBitmap(toCrop, m, paint);
            return result;
        }
    

    这里的逻辑交给读者自行分析,毕竟不是Glide的主干逻辑,暂且略过。
    我们回到into()方法的分析中。在into()方法中,最终返回了 into(glide.buildImageViewTarget(view, transcodeClass)),这里的buildImageViewTarget()方法是构建Target实例的核心,让我们看一下方法的逻辑实现:

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

    这里的imageViewTargetFactory即ImageViewTarget的工厂类,负责创建ImageViewTarget实例,最终在ImageViewTargetFactory类中找到构建Target实例的逻辑:

    ImageViewTargetFactory.java
        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)");
            }
        }
    }
    
    注:isAssignableFrom()方法用于判断Class对象所表示的类或接口与指定的Class参数所表示的类或接口是否相同。若B extends A,或 B == A,则A.isAssignableFrom(B)返回true,反之,返回false。

    可以发现buildTarget(ImageView view, Class<Z> clazz)方法根据传入不同的clazz对象来构建不同类型的ImageViewTarget对象。那么这个clazz对象是从哪里传进来的呢?读者可以试着从源码中追踪看看,这里两行哥直接告诉答案:在第二篇中我们分析load()方法,其返回值类型为DrawableTypeRequest<ModelType>,而在DrawableTypeRequest<ModelType>中有两个关键方法asBitmap()和asGif():

    DrawableTypeRequest.java
        public BitmapTypeRequest<ModelType> asBitmap() {
            return optionsApplier.apply(new BitmapTypeRequest<ModelType>(this, streamModelLoader,
                    fileDescriptorModelLoader, optionsApplier));
        }
        public GifTypeRequest<ModelType> asGif() {
            return optionsApplier.apply(new GifTypeRequest<ModelType>(this, streamModelLoader, optionsApplier));
        }
    

    可以发现这两个方法中分别创建了BitmapTypeRequest实例和GifTypeRequest实例。在创建这两种实例的时候,分别传入了Bitmap.class和GifDrawable.class(GifDrawable继承于GlideDrawable),这就是buildTarget(ImageView view, Class<Z> clazz)中传入的clazz。如果用户既没有调用asBitmap()也没有调用asGif(),通过源码可以发现默认传入了Bitmap.class,这里不再展开分析。
    分析到这里,总结一下,调用Glide.with().load().asBitmap()、Glide.with().load().asGif()或没有调用asBitmap()和asGif(),最终会为buildTarget()方法传入两种不同的clazz。根据传入的clazz,buildTarget()方法中的if...else分支最终的返回值有两种:GlideDrawableImageViewTarget实例或BitmapImageViewTarget实例,而DrawableImageViewTarget很少用到,暂不考虑。
    接着分析一下buildTarget()方法返回值Target到底是什么,追踪一下Target类的源码:

    Target.java
    public interface Target<R> extends LifecycleListener {
     
        int SIZE_ORIGINAL = Integer.MIN_VALUE;
    
        void onLoadStarted(Drawable placeholder);
    
        void onLoadFailed(Exception e, Drawable errorDrawable);
    
        void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation);
    
        void onLoadCleared(Drawable placeholder);
    
        void getSize(SizeReadyCallback cb);
    
        void setRequest(Request request);
    
        Request getRequest();
    }
    
    LifecycleListener.java
    public interface LifecycleListener {
    
        void onStart();
    
        void onStop();
    
        void onDestroy();
    }
    

    Target接口继承了LifecycleListener接口。LifecycleListener接口应该熟悉了,在第一篇里我们已经讲解过,之所以Glide可以根据目标Activity或Fragment的生命周期执行对应的操作,是因为RequestManagerFragment的成员变量lifecycle的成员变量Set<LifecycleListener> lifecycleListeners中的每个LifecycleListener对象实现了生命周期的监听(成员变量的成员变量?有点绕......建议读者回看第一篇的源码)。
    在Target接口中定义了更多的监听回调,有多种实现。之前我们讲解过,Target是对需要展示图片的目标ImageView的封装,我们以Target接口的具体实现类ImageViewTarget为例进行分析(ViewTarget实现了Target接口):

    ImageViewTarget.java
    public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
    
        public ImageViewTarget(ImageView view) {
            super(view);
        }
    
        @Override
        public Drawable getCurrentDrawable() {
            return view.getDrawable();
        }
    
        @Override
        public void setDrawable(Drawable drawable) {
            view.setImageDrawable(drawable);
        }
    
        @Override
        public void onLoadStarted(Drawable placeholder) {
            view.setImageDrawable(placeholder);
        }
    
        @Override
        public void onLoadFailed(Exception e, Drawable errorDrawable) {
            view.setImageDrawable(errorDrawable);
        }
    
        @Override
        public void onLoadCleared(Drawable placeholder) {
            view.setImageDrawable(placeholder);
        }
    
        @Override
        public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
            if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
                setResource(resource);
            }
        }
    
        protected abstract void setResource(Z resource);
    
    }
    

    在上文的源码中,实现一些逻辑,比如在加载中显示占位图(onLoadStarted(Drawable placeholder)),在加载失败的时候显示错误图片(onLoadFailed(Exception e, Drawable errorDrawable))等等,源码的最后一行是抽象方法setResource(Z resource)。为什么要定义为抽象方法呢?因为Glide显示的图片可能有不同的来源,因此针对不同来源的图片,定义子类重新覆写该方法即可。以BitmapImageViewTarget为例,看看如何实现这个抽象方法的。

    BitmapImageViewTarget.java
    public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
        public BitmapImageViewTarget(ImageView view) {
            super(view);
        }
    
        /**
         * Sets the {@link android.graphics.Bitmap} on the view using
         * {@link android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.
         *
         * @param resource The bitmap to display.
         */
        @Override
        protected void setResource(Bitmap resource) {
            view.setImageBitmap(resource);
        }
    }
    

    很简单,仅仅是调用了ImageView.setImageBitmap(resource)方法展示了图片。至此,构建Target逻辑完成。

    二、创建Request,传入之前创建的Target

    这里的Request即图片加载请求。看到这里,读者可能产生了混乱,先梳理一下。
    1.Glide.with().load()方法最终返回值类型为DrawableTypeRequest<ModelType>;
    2.DrawableTypeRequest<ModelType>继承于DrawableRequestBuilder<ModelType>;
    3.DrawableRequestBuilder<ModelType>继承于GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>;
    4.GenericRequest<A, T, Z, R>实现了Request接口;
    5.GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable>是对GenericRequest<A, T, Z, R>的创建者模式包装。
    这里面类关联略微复杂,需要读者体味一下。接下来,回头去看上文提及的into()方法:

    GenericRequestBuilder.java
        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));
        }
    

    方法体最后一行return语句调用了into(glide.buildImageViewTarget(view, transcodeClass)),进入源码看看执行了哪些操作。

    GenericRequestBuilder.java
        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;
        }
    

    方法体第一行同样对当前线程是否为主线程进行了判断。继续向下看,不知道大家是否还记得ListView的Adapter用法,为了重复利用ItemView,用viewHolder承载view。第一次创建convertView的时候,调用了convertView.setTag(viewHolder),下次复用convertView的时候,通过convertView.getTag()来获取原本的viewHolder,即将convertView通过setTag()方法与viewHolder一一绑定。这里利用了相似的原理,将target与request进行了一一绑定。首先,调用了target.getRequest()来获取曾经绑定的上一个request对象previous,如果previous存在,则销毁previous。然后通过buildRequest()方法重新创建一个request,并与target进行绑定。
    看看target.getRequest()方法内部的逻辑。

    ViewTarget.java
        public Request getRequest() {
            Object tag = getTag();
            Request request = null;
            if (tag != null) {
                if (tag instanceof Request) {
                    request = (Request) tag;
                } else {
                    throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
                }
            }
            return request;
        }
        ......省略
        @Override
        public void setRequest(Request request) {
            setTag(request);
        }
        ......省略
        private void setTag(Object tag) {
            if (tagId == null) {
                isTagUsedAtLeastOnce = true;
                view.setTag(tag);
            } else {
                view.setTag(tagId, tag);
            }
        }
        ......省略
        private Object getTag() {
            if (tagId == null) {
                return view.getTag();
            } else {
                return view.getTag(tagId);
            }
        }
    

    摘取ViewTarget类中的四个方法getRequest()、setRequest()、setTag()和getTag()方法。之前一直在说,Target是对ImageView的封装,可以发现这两个方法的本质就是通过imageView.getTag()获取request以及通过imageView.setTag(request)绑定request。
    回到上面的into(Y target)方法,继续分析。若target.getRequest()获取到的Request对象不为null(Target已经与一个图片请求相绑定),执行的操作是取消已经绑定的Request对象。previous.clear()主要逻辑是取消加载图片请求previous,requestTracker.removeRequest(previous)主要逻辑是从requestTracker(请求追踪器)对象内部维护的两个请求集合(所有请求集合和等待中请求集合)中移除加载图片的请求previous,previous.recycle()主要逻辑是将previous的成员变量置为null或默认值,如下文源码所示。

    GenericRequest.java
        @Override
        public void clear() {
            Util.assertMainThread();
            if (status == Status.CLEARED) {
                return;
            }
            cancel();
            // Resource must be released before canNotifyStatusChanged is called.
            if (resource != null) {
                releaseResource(resource);
            }
            if (canNotifyStatusChanged()) {
                target.onLoadCleared(getPlaceholderDrawable());
            }
            // Must be after cancel().
            status = Status.CLEARED;
        }
    
        void cancel() {
            status = Status.CANCELLED;
            if (loadStatus != null) {
                loadStatus.cancel();
                loadStatus = null;
            }
        }
    
        @Override
        public void recycle() {
            loadProvider = null;
            model = null;
            context = null;
            target = null;
            placeholderDrawable = null;
            errorDrawable = null;
            fallbackDrawable = null;
            requestListener = null;
            requestCoordinator = null;
            transformation = null;
            animationFactory = null;
            loadedFromMemoryCache = false;
            loadStatus = null;
            REQUEST_POOL.offer(this);
        }
    
    RequestTracker.java
        private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());//所有请求集合
        private final List<Request> pendingRequests = new ArrayList<Request>();//等待中请求集合
        public void removeRequest(Request request) {
            requests.remove(request);
            pendingRequests.remove(request);
        }
    

    稍微提一下recycle() 方法,该方法最后一行调用了REQUEST_POOL.offer(this)将该请求对象加入请求池队列以备复用。
    继续回到into()方法,在销毁了上一个Request后,执行buildRequest(target)方法来创建新的Request,看一下实现逻辑:

    GenericRequestBuilder.java
        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(Target<TranscodeType> target) 方法内最终执行了buildRequestRecursive(target, null)。buildRequestRecursive()方法比较复杂,不过前面绝大部分语句都是与缩略图有关的逻辑,跳过不看,只看最后一个else分支:return obtainRequest(target, sizeMultiplier, priority, parentCoordinator)。跟进obtainRequest()方法查看实现逻辑,执行了GenericRequest.obtain()方法并传入了大量参数,查看一下这个方法的源码:

    GenericRequest.java
        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;
        }
    
        private void init(
                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) {
            this.loadProvider = loadProvider;
            this.model = model;
            this.signature = signature;
            this.fallbackDrawable = fallbackDrawable;
            this.fallbackResourceId = fallbackResourceId;
            this.context = context.getApplicationContext();
            this.priority = priority;
            this.target = target;
            this.sizeMultiplier = sizeMultiplier;
            this.placeholderDrawable = placeholderDrawable;
            this.placeholderResourceId = placeholderResourceId;
            this.errorDrawable = errorDrawable;
            this.errorResourceId = errorResourceId;
            this.requestListener = requestListener;
            this.requestCoordinator = requestCoordinator;
            this.engine = engine;
            this.transformation = transformation;
            this.transcodeClass = transcodeClass;
            this.isMemoryCacheable = isMemoryCacheable;
            this.animationFactory = animationFactory;
            this.overrideWidth = overrideWidth;
            this.overrideHeight = overrideHeight;
            this.diskCacheStrategy = diskCacheStrategy;
            status = Status.PENDING;
    
            // We allow null models by just setting an error drawable. Null models will always have empty providers, we
            // simply skip our sanity checks in that unusual case.
            if (model != null) {
                check("ModelLoader", loadProvider.getModelLoader(), "try .using(ModelLoader)");
                check("Transcoder", loadProvider.getTranscoder(), "try .as*(Class).transcode(ResourceTranscoder)");
                check("Transformation", transformation, "try .transform(UnitTransformation.get())");
                if (diskCacheStrategy.cacheSource()) {
                    check("SourceEncoder", loadProvider.getSourceEncoder(),
                            "try .sourceEncoder(Encoder) or .diskCacheStrategy(NONE/RESULT)");
                } else {
                    check("SourceDecoder", loadProvider.getSourceDecoder(),
                            "try .decoder/.imageDecoder/.videoDecoder(ResourceDecoder) or .diskCacheStrategy(ALL/SOURCE)");
                }
                if (diskCacheStrategy.cacheSource() || diskCacheStrategy.cacheResult()) {
                    // TODO if(resourceClass.isAssignableFrom(InputStream.class) it is possible to wrap sourceDecoder
                    // and use it instead of cacheDecoder: new FileToStreamDecoder<Z>(sourceDecoder)
                    // in that case this shouldn't throw
                    check("CacheDecoder", loadProvider.getCacheDecoder(),
                            "try .cacheDecoder(ResouceDecoder) or .diskCacheStrategy(NONE)");
                }
                if (diskCacheStrategy.cacheResult()) {
                    check("Encoder", loadProvider.getEncoder(),
                            "try .encode(ResourceEncoder) or .diskCacheStrategy(NONE/SOURCE)");
                }
            }
        }
    

    看着代码特别长,其实很简单,就是创建了Request对象,并调用request.init()方法将大量的参数进行初始化赋值。留意一下obtain()方法体第一行,调用 (GenericRequest<A, T, Z, R>)REQUEST_POOL.poll()来获取请求池队列中曾经缓存的Request请求,如果获取到的结果为null,则直接通过构造方法创建一个GenericRequest对象。如果获取到的Request请求不为null,则复用。至此,创建Request的逻辑分析完毕。

    三、执行Request,计算图片宽高

    Request对象创建好了之后,到底是如何执行的呢?回到之前的into()方法。

    GenericRequestBuilder.java
        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;
        }
    

    倒数第二行requestTracker.runRequest(request)执行了创建好的Request对象,看看RequestTracker类runRequest()方法做了哪些操作。

    RequestTracker.java
        private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());//所有请求集合
        private final List<Request> pendingRequests = new ArrayList<Request>();//等待中请求集合
        public void runRequest(Request request) {
            requests.add(request);
            if (!isPaused) {
                request.begin();
            } else {
                pendingRequests.add(request);
            }
        }
    

    首先将request加入到所有请求集合requests中。如果当前RequestTracker处于暂停状态,则将request加入到等待中请求集合pendingRequests中,如果当前Request不处于暂停状态,则调用begin()方法开始执行请求。让我们继续追踪begin()方法内部的逻辑。

    GenericRequest.java
        @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));
            }
        }
    

    这里主要看一下如下核心代码:

    GenericRequest.java
            if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
                onSizeReady(overrideWidth, overrideHeight);
            } else {
                target.getSize(this);
            }
    

    用户在调用override(width,height)时对overrideWidth与overrideHeight进行赋值(设置了固定的宽高),其初始值为0,Util.isValidDimensions()是用于校验是否是有效的覆写宽高,如果返回值为true则调用onSizeReady(overrideWidth, overrideHeight),否则调用 target.getSize()来获取图片的实际宽高。先来看看getSize()中做了哪些操作:

    ViewTarget.java
        public void getSize(SizeReadyCallback cb) {
                int currentWidth = getViewWidthOrParam();
                int currentHeight = getViewHeightOrParam();
                if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
                    cb.onSizeReady(currentWidth, currentHeight);
                } else {
                    // We want to notify callbacks in the order they were added and we only expect one or two callbacks to
                    // be added a time, so a List is a reasonable choice.
                    if (!cbs.contains(cb)) {
                        cbs.add(cb);
                    }
                    if (layoutListener == null) {
                        final ViewTreeObserver observer = view.getViewTreeObserver();
                        layoutListener = new SizeDeterminerLayoutListener(this);
                        observer.addOnPreDrawListener(layoutListener);
                    }
                }
            }
    

    看源码逻辑,首先调用getViewWidthOrParam()和getViewHeightOrParam()获取图片的宽高,通常情况下是可以获取到有效的宽高值(当UI绘制完毕),直接回调onSizeReady(currentWidth, currentHeight)。但是如果在UI没有绘制完毕,获取到的宽高可能为int常量PENDING_SIZE,即0。这时就会走到第一个else分支,获取了View的viewThreeObserver,并为此添加了监听addOnPreDrawListener(SizeDeterminerLayoutListener layoutListener)。接下来看看SizeDeterminerLayoutListener到底做了哪些监听。

    ViewTarget.java
            private static class SizeDeterminerLayoutListener implements ViewTreeObserver.OnPreDrawListener {
                private final WeakReference<SizeDeterminer> sizeDeterminerRef;
    
                public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
                    sizeDeterminerRef = new WeakReference<SizeDeterminer>(sizeDeterminer);
                }
    
                @Override
                public boolean onPreDraw() {
                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
                        Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
                    }
                    SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
                    if (sizeDeterminer != null) {
                        sizeDeterminer.checkCurrentDimens();
                    }
                    return true;
                }
            }
    

    可以看到SizeDeterminerLayoutListener对象持有了弱引用sizeDeterminer对象,便于GC在不需要的时候进行回收。重点看一下sizeDeterminer.checkCurrentDimens()逻辑。

    ViewTarget.java
           private void checkCurrentDimens() {
                if (cbs.isEmpty()) {
                    return;
                }
    
                int currentWidth = getViewWidthOrParam();
                int currentHeight = getViewHeightOrParam();
                if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight)) {
                    return;
                }
    
                notifyCbs(currentWidth, currentHeight);
           
                ViewTreeObserver observer = view.getViewTreeObserver();
                if (observer.isAlive()) {
                    observer.removeOnPreDrawListener(layoutListener);
                }
                layoutListener = null;
            }
    
             private void notifyCbs(int width, int height) {
                for (SizeReadyCallback cb : cbs) {
                    cb.onSizeReady(width, height);
                }
                cbs.clear();
            }
    

    可以看到在获取到宽高后,执行了notifyCbs(currentWidth,currentHeight)来回调有效宽高,最终同上文一样,还是调用了onSizeReady()方法。也就是说不管走到begin()方法内部的哪个分支,最终都会回调onSizeReady()方法。

    四、加载及解析图片

    上文讲到,无论何种情况,都会走到onSizeReady()方法。进入onSizeReady()方法跟进源码,为了方便阅读,这里只保留核心代码:

    GenericRequest.java
        @Override
        public void onSizeReady(int width, int 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();
            ......省略
            loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this);
            ......省略
        }
    

    这里看到了熟悉的对象:ModelLoader和ResourceTranscoder(不熟悉?请参阅第一篇)。这里主要的逻辑是通过loadProvider.getModelLoader()方法获取到ModelLoader实例和loadProvider.getTranscoder()方法获取到ResourceTranscoder实例。那么loadProvider又是什么对象呢?追踪一下源码,回到BitmapTypeRequest类中。

    BitmapTypeRequest.java
        BitmapTypeRequest(GenericRequestBuilder<ModelType, ?, ?, ?> other,
                ModelLoader<ModelType, InputStream> streamModelLoader,
                ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader,
                RequestManager.OptionsApplier optionsApplier) {
            super(buildProvider(other.glide, streamModelLoader, fileDescriptorModelLoader, Bitmap.class, null),
                    Bitmap.class, other);
            this.streamModelLoader = streamModelLoader;
            this.fileDescriptorModelLoader = fileDescriptorModelLoader;
            this.glide = other.glide;
            this.optionsApplier = optionsApplier;
        }
    

    在BitmapTypeRequest的构造方法中,调用了buildProvider()方法创建了LoadProvider对象,看一下方法内部的逻辑:

        private static <A, R> FixedLoadProvider<A, ImageVideoWrapper, Bitmap, R> buildProvider(Glide glide,
                ModelLoader<A, InputStream> streamModelLoader,
                ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader,
                Class<R> transcodedClass, ResourceTranscoder<Bitmap, R> transcoder) {
            if (streamModelLoader == null && fileDescriptorModelLoader == null) {
                return null;
            }
    
            if (transcoder == null) {
                transcoder = glide.buildTranscoder(Bitmap.class, transcodedClass);
            }
            DataLoadProvider<ImageVideoWrapper, Bitmap> loadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
                    Bitmap.class);
            ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
                    fileDescriptorModelLoader);
    
            return new FixedLoadProvider<A, ImageVideoWrapper, Bitmap, R>(modelLoader, transcoder, loadProvider);
        }
    

    看最后一行,在创建FixedLoadProvider实例的构造方法中传入了modelLoader(在modelLoader的构造方法中传入了流ModelLoader:streamModelLoader和文档描述符ModelLoader:fileDescriptorModelLoader)、transcoder、loadProvider对象,再看看FixedLoadProvider类中查一下源码:

    FixedLoadProvider.java
    public class FixedLoadProvider<A, T, Z, R> implements LoadProvider<A, T, Z, R>  {
        private final ModelLoader<A, T> modelLoader;
        private final ResourceTranscoder<Z, R> transcoder;
        private final DataLoadProvider<T, Z> dataLoadProvider;
    
        public FixedLoadProvider(ModelLoader<A, T> modelLoader, ResourceTranscoder<Z, R> transcoder,
                DataLoadProvider<T, Z> dataLoadProvider) {
            if (modelLoader == null) {
                throw new NullPointerException("ModelLoader must not be null");
            }
            this.modelLoader = modelLoader;
    
            if (transcoder == null) {
                throw new NullPointerException("Transcoder must not be null");
            }
            this.transcoder = transcoder;
    
            if (dataLoadProvider == null) {
                throw new NullPointerException("DataLoadProvider must not be null");
            }
            this.dataLoadProvider = dataLoadProvider;
        }
    }
    

    看到这里已经明白了,LoadProvider其实就是对ModelLoader、ResourceTranscoder、DataLoadProvider的封装,持有了这三种类型的成员变量,便于统一管理。
    现在回到onSizeReady()方法,分析一下通过LoadProvider获取到的ModelLoader实例和ResourceTranscoder实例的主要作用。前文说过,ModelLoader实例负责将源数据(Model)转化为输入流(Data),往细了说,ModelLoader实例中具体承担此项工作的就是DataFetcher实例。DataFetcher是一个接口,有多种实现类,如下图所示:


    DataFetcher实现类

    之所以有这么多种实现类,是因为有多种Model,需要将其转化为Data就需要通过不同的手段,比如从Asset文件(AssetPathFetcher)、从File文件(FileDescriptorLocalUriFetcher)、从网络(HttUrlFetcher)等。
    现在我们以ModelLoader的具体实现子类HttpUrlGlideUrlLoader为例,可以看到getResourceFetcher()方法返回了一个HttpUrlFetcher实例:

    HttpUrlGlideUrlLoader.java
    public class HttpUrlGlideUrlLoader implements StreamModelLoader<GlideUrl> {
    
        private final ModelCache<GlideUrl, GlideUrl> modelCache;
    
        @Override
        public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
            // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time spent parsing urls.
            GlideUrl url = model;
            if (modelCache != null) {
                url = modelCache.get(model, 0, 0);
                if (url == null) {
                    modelCache.put(model, 0, 0, model);
                    url = model;
                }
            }
            return new HttpUrlFetcher(url);
        }
    }
    

    看到这里可能有疑问,DataFetcher是如何将Model转化为Data(即输入流)呢?还是以HttpUrlFetcher为例,追踪一下源码:

    HttpUrlFetcher.java
        @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;
        }
    

    上文摘录了HttpUrlFetcher类中获取输入流的核心方法loadData()。虽然在onSizeReady()方法中并没有直接调用HttpUrlFetcher实例的loadData()方法(在另外的方法中调用了),但是我们可以先分析一下loadData()的逻辑。在loadData()内部调用了loadDataWithRedirects()方法。可以看到loadDataWithRedirects()方法内部创建了urlConnection并调用connect(),如果返回的状态码为200,则调用getStreamForSuccessfulRequest()方法来取得的InputStream,如果返回的状态码为300,则获取重定向url,重新调用loadDataWithRedirects()方法。至此DataFetcher类中Model转Data(输入流)的逻辑已经分析完毕。
    回到onSizeReady()方法,这里通过loadProvider.getTranscoder()获取的transcoder作用是将DataFetcher获取到的Data(输入流)转为Resource。终于看见onSizeReady()中的核心方法engine.load()方法了。engine是Engine类的实例,Engine类是管理图片获取及加载任务的类。可以看到在engine.load()方法中传入了上文已经分析过的DataFetcher实例、LoadProvider实例以及ResourceTransCoder实例。那么engine.load()这个核心方法内部到底做了什么操作呢?下篇文章再做分析。

    碍于篇幅,暂且分析到这里,主要分析了Glide内部的一些准备操作:
    1.构建Target
    2.创建Request
    3.执行Request,计算图片宽高
    4.通过Model获取图片的输入流
    分析了这么多篇,居然还没到Glide核心加载图片的逻辑?稍安勿躁,我们下篇文章再见。

    相关文章

      网友评论

      本文标题:〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(三)

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