Android开发经验谈Android开发

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

2018-04-27  本文已影响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核心加载图片的逻辑?稍安勿躁,我们下篇文章再见。
上一篇下一篇

猜你喜欢

热点阅读