Glide into()都做了什么

先看看into(ImageView)

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

先断言是不是主线程,如果没设isTransformationSet就考虑采用ImageView的ScaleType,不过Glide也就只有两种方式:applyCenterCrop,applyFitCenter 而它的具体实现体现在子类:来看看DrawableRequestBuilder.java


  public DrawableRequestBuilder<ModelType> centerCrop() {
        return transform(glide.getDrawableCenterCrop());
    }

      public DrawableRequestBuilder<ModelType> fitCenter() {
            return transform(glide.getDrawableFitCenter());
        }

最终会调用GenericRequestBuilder.transform,前面我们讲过一点transform api,这里我们再深入讲解

applyCenterCrop->调用到transform而transform用到的是Glide的CenterCrop:

Glide.java初始化的时候会初始化bitmapFitCenter


bitmapFitCenter = new FitCenter(bitmapPool);
 drawableCenterCrop = new GifBitmapWrapperTransformation(bitmapPool, bitmapCenterCrop);

GifBitmapWrapperTransformation是处理两种图片资源的包装类


 GifBitmapWrapperTransformation(Transformation<Bitmap> bitmapTransformation,
            Transformation<GifDrawable> gifDataTransformation) {
        this.bitmapTransformation = bitmapTransformation;
        this.gifDataTransformation = gifDataTransformation;
    }

    @Override
     public Resource<GifBitmapWrapper> transform(Resource<GifBitmapWrapper> resource, int outWidth, int outHeight) {
         Resource<Bitmap> bitmapResource = resource.get().getBitmapResource();
         Resource<GifDrawable> gifResource = resource.get().getGifResource();
         if (bitmapResource != null && bitmapTransformation != null) {
             Resource<Bitmap> transformed = bitmapTransformation.transform(bitmapResource, outWidth, outHeight);
             if (!bitmapResource.equals(transformed)) {
                 GifBitmapWrapper gifBitmap = new GifBitmapWrapper(transformed, resource.get().getGifResource());
                 return new GifBitmapWrapperResource(gifBitmap);
             }
         } else if (gifResource != null && gifDataTransformation != null) {
             Resource<GifDrawable> transformed = gifDataTransformation.transform(gifResource, outWidth, outHeight);
             if (!gifResource.equals(transformed)) {
                 GifBitmapWrapper gifBitmap = new GifBitmapWrapper(resource.get().getBitmapResource(), transformed);
                 return new GifBitmapWrapperResource(gifBitmap);
             }
         }
         return resource;
     }

我们重点关注bitmapTransformation的实现也就是FitCenter实现

FitCenter.java


public class FitCenter extends BitmapTransformation {

    public FitCenter(Context context) {
        super(context);
    }

    public FitCenter(BitmapPool bitmapPool) {
        super(bitmapPool);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight);
    }

    @Override
    public String getId() {
        return "FitCenter.com.bumptech.glide.load.resource.bitmap";
    }
}

再看看TransformationUtils.java


 public static Bitmap fitCenter(Bitmap toFit, BitmapPool pool, int width, int height) {
        if (toFit.getWidth() == width && toFit.getHeight() == height) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "requested target size matches input, returning input");
            }
            return toFit;
        }
        final float widthPercentage = width / (float) toFit.getWidth();
        final float heightPercentage = height / (float) toFit.getHeight();
        final float minPercentage = Math.min(widthPercentage, heightPercentage);

        // take the floor of the target width/height, not round. If the matrix
        // passed into drawBitmap rounds differently, we want to slightly
        // overdraw, not underdraw, to avoid artifacts from bitmap reuse.
        final int targetWidth = (int) (minPercentage * toFit.getWidth());
        final int targetHeight = (int) (minPercentage * toFit.getHeight());

        if (toFit.getWidth() == targetWidth && toFit.getHeight() == targetHeight) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "adjusted target size matches input, returning input");
            }
            return toFit;
        }

        Bitmap.Config config = getSafeConfig(toFit);
        Bitmap toReuse = pool.get(targetWidth, targetHeight, config);
        if (toReuse == null) {
            toReuse = Bitmap.createBitmap(targetWidth, targetHeight, config);
        }
        // We don't add or remove alpha, so keep the alpha setting of the Bitmap we were given.
        TransformationUtils.setAlpha(toFit, toReuse);

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "request: " + width + "x" + height);
            Log.v(TAG, "toFit:   " + toFit.getWidth() + "x" + toFit.getHeight());
            Log.v(TAG, "toReuse: " + toReuse.getWidth() + "x" + toReuse.getHeight());
            Log.v(TAG, "minPct:   " + minPercentage);
        }

        Canvas canvas = new Canvas(toReuse);
        Matrix matrix = new Matrix();
        matrix.setScale(minPercentage, minPercentage);
        Paint paint = new Paint(PAINT_FLAGS);
        canvas.drawBitmap(toFit, matrix, paint);

        return toReuse;
    }

先去算出targetWidth,和targetHeight,其原理就是,看宽高比谁大,展示bitmap的宽比高多的多,就以高适配宽,反之以宽适配高 然后是通过bitmapPool去找到有没有这个targetWidth,targetHeight的缓存,有就拿出来复用,没有就创建新的。

BitmapPool初始化源码:

Glide.java


 if (bitmapPool == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                int size = calculator.getBitmapPoolSize();
                bitmapPool = new LruBitmapPool(size);
            } else {
                bitmapPool = new BitmapPoolAdapter();
            }
        }

LruBitmapPool的源码后面的章节再讲.

继续关注into


into(glide.buildImageViewTarget(view, transcodeClass))

通过glide创建了一个target,且把transcodeClass传进去了

再看Glide.java实现buildImageViewTarget

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


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

有三种ImageViewTarget,生成target之后

 public <Y extends Target<TranscodeType>> Y into(Y target) {
        Util.assertMainThread();
        if (target == null) {
            throw new IllegalArgumentException("You must pass in a non null Target");
        }
        if (!isModelSet) {
            throw new IllegalArgumentException("You must first set a model (try #load())");
        }

        Request previous = target.getRequest();

        if (previous != null) {
            previous.clear();
            requestTracker.removeRequest(previous);
            previous.recycle();
        }

        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        requestTracker.runRequest(request);

        return target;
    }

它会把request和target进行绑定如果target之前有request就解绑,并回收它,然后创建新的request 这里的创建非常有意思,它是通过一个池子来获取,recycle的时候会把所有属性都赋值为空,并回收到池子中。

注意lifecycle.addListener(target);这里的lifecycle如果是Activity的话,它就是ActivityFragmentLifecycle,我们知道activity会把它的生命周期传递给ActivityFragmentLifecycle,然后ActivityFragmentLifecycle会分发给target

target如同一个请求落地页,请求之后,通过target去处理请求结果

target家族(部分):

target

简单说一下ViewTarget怎么通过SizeDeterminer处理getSize的: 当ViewTarget的getSize被调用的时候会调用SizeDeterminer的getSize,如果此时没拿到(通过layoutParm拿失败了,或没走onMeasure)</br> 它会通过一个list记录回调,当走到preDraw的时候会通知所有回调者它测量成功了

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

SizeDeterminerLayoutListener是用来监听preDraw的。WeakReference通过弱引用的方式避免内存不被释放的风险。


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

当然要记得removeListener

      private void checkCurrentDimens() {
            if (cbs.isEmpty()) {
                return;
            }

            int currentWidth = getViewWidthOrParam();
            int currentHeight = getViewHeightOrParam();
            if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight)) {
                return;
            }

            notifyCbs(currentWidth, currentHeight);
            // Keep a reference to the layout listener and remove it here
            // rather than having the observer remove itself because the observer
            // we add the listener to will be almost immediately merged into
            // another observer and will therefore never be alive. If we instead
            // keep a reference to the listener and remove it here, we get the
            // current view tree observer and should succeed.
            ViewTreeObserver observer = view.getViewTreeObserver();
            if (observer.isAlive()) {
                observer.removeOnPreDrawListener(layoutListener);
            }
            layoutListener = null;
        }

Request的创建通过一个队列ArrayDeque进行管理

    public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(

    //...
          GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
          if (request == null) {
              request = new GenericRequest<A, T, Z, R>();
          }
        //...
      }

    @Override
      public void recycle() {
         //...
          REQUEST_POOL.offer(this);
      }

request的api

Request

RequestTracker.runRequest会调到request.begin()


 /**
     * {@inheritDoc}
     */
    @Override
    public void begin() {
        startTime = LogTime.getLogTime();
        if (model == null) {
            onException(null);
            return;
        }

        status = Status.WAITING_FOR_SIZE;
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            onSizeReady(overrideWidth, overrideHeight);
        } else {
            target.getSize(this);
        }

        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
            target.onLoadStarted(getPlaceholderDrawable());
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
        }
    }

如果界面没有measure,走的是getSize,后面布局成功会走onSizeReady,onSizeReady才会真的走加载逻辑,然后如果图片没加载成功,就通知target用Placeholder

onSizeReady:

如果顺利拿到宽高,就开始走加载逻辑:

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

Engine.load开启加载模式

先生成一个key,可以这么说,只要request的所有属性相同,那么它的key一定相同,有兴趣可以看看它们的实现,这里不做过多的解释

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

先从内存缓存里找,如果内存缓存里有,就直接回调给target.


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

然后从活跃缓存里找,如果找到也直接回调给target

cache内存缓存的内容在其它章节里讲,本章重点讲它没有缓存的情况

然后看看有没有重复的job,如果有重复的job,直接把回调加给EngineJob,这样,当这个job结束的时候会告诉所有回调者

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) {
      //...缓存略过

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

     //... 真正取图片略过
    }

如果任务里有这个Job那就返回一个LoadStatus,LoadStatus只是callback和job的结合体,并还有解绑的功能

    /**
     * Allows a request to indicate it no longer is interested in a given load.
     */
    public static class LoadStatus {
        private final EngineJob engineJob;
        private final ResourceCallback cb;

        public LoadStatus(ResourceCallback cb, EngineJob engineJob) {
            this.cb = cb;
            this.engineJob = engineJob;
        }

        public void cancel() {
            engineJob.removeCallback(cb);
        }
    }

如果没有找到重复的job就会build新的job,通过factory创建,此factory拥有两个线程池,它是从Glide初始化的时候得来的

    static class EngineJobFactory {
        private final ExecutorService diskCacheService;
        private final ExecutorService sourceService;
        private final EngineJobListener listener;

        public EngineJobFactory(ExecutorService diskCacheService, ExecutorService sourceService,
                EngineJobListener listener) {
            this.diskCacheService = diskCacheService;
            this.sourceService = sourceService;
            this.listener = listener;
        }

        public EngineJob build(Key key, boolean isMemoryCacheable) {
            return new EngineJob(key, diskCacheService, sourceService, isMemoryCacheable, listener);
        }
    }

Glide.java

 if (sourceService == null) {
            final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
            sourceService = new FifoPriorityThreadPoolExecutor(cores);
        }
        if (diskCacheService == null) {
            diskCacheService = new FifoPriorityThreadPoolExecutor(1);
        }

然后是初始化了decodeJob及runnable.decodeJob集成了decode,encode一系列的工作代码,坐等run

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

然后就是engineJob.start(runnable);

EngineJob.java

   public void start(EngineRunnable engineRunnable) {
        this.engineRunnable = engineRunnable;
        future = diskCacheService.submit(engineRunnable);
    }

EngineRunnable的run方法

EngineRunnable.java

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

首先分清几个概念getEncoder,getSourceEncoder,getCacheDecoder,getSourceDecoder

writeTransformedToCache-getEncoder:transformed后的图片保存本地

cacheAndDecodeSourceData-getSourceEncoder:直接把网络流保存成本地流

loadFromCache-getCacheDecoder:本地流转成Bitmap,不管是原图还是形变后的图

decodeFromSourceData-getSourceDecoder:当缓存策略为不缓存的时候,直接把网络流转成图片 :flushed:

可以通过一个DataLoadProvider的构造猜出来:(当然后面会拿出证据)

   public StreamBitmapDataLoadProvider(BitmapPool bitmapPool, DecodeFormat decodeFormat) {
        sourceEncoder = new StreamEncoder();//输入流保存成文件
        sourceDecoder = new StreamBitmapDecoder(bitmapPool, decodeFormat);//输入流转成bitmap
        encoder = new BitmapEncoder();//把bitmap保存成文件
        cacheDecoder = new FileToStreamDecoder<Bitmap>(decoder);//File转成bitmap
    }

EngineRunnable有两个过程:Stage.CACHE,Stage.SOURCE

当EngineRunnable初始化的时候Stage=CACHE

  public EngineRunnable(EngineRunnableManager manager, DecodeJob<?, ?, ?> decodeJob, Priority priority) {
  //..
        this.stage = Stage.CACHE;
  //..
    }

那第一次run的时候是在diskCacheService的线程池里进行,接着走decode分两种情况,如果stage=cache就走缓存

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

先decode Transform后的图片,没有再考虑用原图

    private Resource<?> decodeFromCache() throws Exception {
        Resource<?> result = null;
        try {
            result = decodeJob.decodeResultFromCache();
        } catch (Exception e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Exception decoding result from cache: " + e);
            }
        }

        if (result == null) {
            result = decodeJob.decodeSourceFromCache();
        }
        return result;
    }

优先走transform 后的图上也就是resultFromCache

  public Resource<Z> decodeResultFromCache() throws Exception {
        if (!diskCacheStrategy.cacheResult()) {//如果不缓存transformed就直接返回null了
            return null;
        }

        //..
        Resource<T> transformed = loadFromCache(resultKey);
       //..
        Resource<Z> result = transcode(transformed);//此处做了transcode,把GifBitmapWrapper转成GlideBitmapDrawable
       //..
        return result;
    }

如果没有就走原图

 public Resource<Z> decodeSourceFromCache() throws Exception {
        if (!diskCacheStrategy.cacheSource()) {
            return null;
        }

       //..
        Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
        //..
        return transformEncodeAndTranscode(decoded);
    }

用原图和用transform之后的图,区别仅仅是原图的话再做一次transform

loadFromCache则是把file转成Resource

注:loadFromCache用的是cacheDecoder

private Resource<T> loadFromCache(Key key) throws IOException {
        File cacheFile = diskCacheProvider.getDiskCache().get(key);
        if (cacheFile == null) {
            return null;
        }

        Resource<T> result = null;
        try {
            result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
        } finally {
            if (result == null) {
                diskCacheProvider.getDiskCache().delete(key);
            }
        }
        return result;
    }

如果读取缓存失败,则会走onLoadFailed,然后把stage变成Source,再通过Source线程池去重新走一次EngineRunnable

private void onLoadFailed(Exception e) {
        if (isDecodingFromCache()) {
            stage = Stage.SOURCE;
            manager.submitForSource(this);
        } else {
            manager.onException(e);
        }
}

重新run就是走decodeFromSource

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

decodeSource里fetcher登场,它就是真正去取数据的工具,取完之后走decodeFromSourceData

    private Resource<T> decodeSource() throws Exception {
        Resource<T> decoded = null;
        try {
           //..
            final A data = fetcher.loadData(priority);
            //..
            decoded = decodeFromSourceData(data);
        } finally {
            fetcher.cleanup();
        }
        return decoded;
    }

decodeFromSourceData方法里先看要不要保存源文件:

如果要保存走保存流程,保存完后再从缓存里读出来给target

如果不要保存,就直接通过SourceDecoder去解析

  private Resource<T> decodeFromSourceData(A data) throws IOException {
        final Resource<T> decoded;
        if (diskCacheStrategy.cacheSource()) {
            decoded = cacheAndDecodeSourceData(data);
        } else {
        //..
            decoded = loadProvider.getSourceDecoder().decode(data, width, height);
        //..
        }
        return decoded;
    }

先通过getSourceEncoder保存本地,再通过loadFromCache(getCacheDecoder)把缓存里的读出来

   private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
        SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
        diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
        Resource<T> result = loadFromCache(resultKey.getOriginalKey());
        return result;
    }

最后原图decode成功会走transform也就transformEncodeAndTranscode方法

 private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
        Resource<T> transformed = transform(decoded);
        writeTransformedToCache(transformed);
        Resource<Z> result = transcode(transformed);
        return result;
    }

把转换后的图存本地的时候用到了getEncoder

    private void writeTransformedToCache(Resource<T> transformed) {
        if (transformed == null || !diskCacheStrategy.cacheResult()) {
            return;
        }
        SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
        diskCacheProvider.getDiskCache().put(resultKey, writer);
    }

into的流程大致就是这样,先走内存缓存内存没有,再走sd卡缓存,如果sd卡缓存没有,再走网络(也有可能是别的途径)。