Glide之旅 —— DecodeJob

前言

glide是谷歌推荐的Android图片加载框架,其优秀的缓存策略、Activity的生命周期的继承、GIF图片的支持等都是为人所称道的地方。下面是用glide加载一张图片的调用。

private void loadImage() {
    Glide.with(this)
         .load("http://pic2.orsoon.com/2017/0118/20170118011032176.jpg")
         .into(ivTest);
}

那么,该框架是如何实际运作的呢,我会通过“Glide之旅”系列博客尽可能详细地将我的心得记录下来。“Glide之旅”系列文章汇总:

概述

EngineJob(com.bumptech.glide.load.engine.DecodeJob)是整个glide开始进行图片编解码、缓存等一系列的动作的发动机。

源码分析

首先明确一点,在DecodeJob中初始化变量的值是来源于 com.bumptech.glide.request.SingleRequest

package com.bumptech.glide.request;

...

public final class SingleRequest<R> implements Request,
        SizeReadyCallback,
        ResourceCallback,
        FactoryPools.Poolable {

    ...

    @Override
    public void onSizeReady(int width, int height) {

        ...

        loadStatus = engine.load(
                glideContext,
                model,
                requestOptions.getSignature(),
                this.width,
                this.height,
                requestOptions.getResourceClass(),
                transcodeClass,
                priority,
                requestOptions.getDiskCacheStrategy(),
                requestOptions.getTransformations(),
                requestOptions.isTransformationRequired(),
                requestOptions.getOptions(),
                requestOptions.isMemoryCacheable(),
                requestOptions.getUseUnlimitedSourceGeneratorsPool(),
                requestOptions.getOnlyRetrieveFromCache(),
                this);

        ...

    }

    ...

}

先来看看执行解码操作的入口函数:

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    @Override
    public void run() {
        try {
            if (isCancelled) {
                notifyFailed();
                return;
            }
            runWrapped();
        } catch (RuntimeException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "DecodeJob threw unexpectedly"
                        + ", isCancelled: " + isCancelled
                        + ", stage: " + stage, e);
            }
            if (stage != Stage.ENCODE) {
                notifyFailed();
            }
            if (!isCancelled) {
                throw e;
            }
        }
    }

    ...

}

也就是说,一般情况下,直接跳转到runWrapped()中去:

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    private void runWrapped() {
        switch (runReason) {
            case INITIALIZE:
                stage = getNextStage(Stage.INITIALIZE);
                currentGenerator = getNextGenerator();
                runGenerators();
                break;
            case SWITCH_TO_SOURCE_SERVICE:
                runGenerators();
                break;
            case DECODE_DATA:
                decodeFromRetrievedData();
                break;
            default:
                throw new IllegalStateException("Unrecognized run reason: " + runReason);
        }
    }

    ...

}

runReason在初始状态下设定为INITIALIZE,这个时候再看一下getNextGenerator(Stage.INITIALIZE)和getNextGenerator():

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    private DataFetcherGenerator getNextGenerator() {
        switch (stage) {
            case RESOURCE_CACHE:
                return new ResourceCacheGenerator(decodeHelper, this);
            case DATA_CACHE:
                return new DataCacheGenerator(decodeHelper, this);
            case SOURCE:
                return new SourceGenerator(decodeHelper, this);
            case FINISHED:
                return null;
            default:
                throw new IllegalStateException("Unrecognized stage: " + stage);
        }
    }

    ...

    private Stage getNextStage(Stage current) {
        switch (current) {
            case INITIALIZE:
                return diskCacheStrategy.decodeCachedResource()
                        ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
            case RESOURCE_CACHE:
                return diskCacheStrategy.decodeCachedData()
                        ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
            case DATA_CACHE:
                return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
            case SOURCE:
            case FINISHED:
                return Stage.FINISHED;
            default:
                throw new IllegalArgumentException("Unrecognized stage: " + current);
        }
    }

    ...

}

由于默认情况下,diskCacheStrategy的值为DiskCacheStrategy.AUTOMATIC:

package com.bumptech.glide.load.engine;

...

public abstract class DiskCacheStrategy {

    ...

    public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
        @Override
        public boolean isDataCacheable(DataSource dataSource) {
            return dataSource == DataSource.REMOTE;
        }

        @Override
        public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
                                        EncodeStrategy encodeStrategy) {
            return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
                    || dataSource == DataSource.LOCAL)
                    && encodeStrategy == EncodeStrategy.TRANSFORMED;
        }

        @Override
        public boolean decodeCachedResource() {
            return true;
        }

        @Override
        public boolean decodeCachedData() {
            return true;
        }
    };

    ...

}

由上面可以分析出,在runReason为INITIALIZE的情况下,stage此时为RESOURCE_CACHE,currentGenerator为ResourceCacheGenerator的实例,再回到runWrapped()中去,接下来调用runGenerators():

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    private void runGenerators() {
        currentThread = Thread.currentThread();
        startFetchTime = LogTime.getLogTime();
        boolean isStarted = false;
        while (!isCancelled && currentGenerator != null
                && !(isStarted = currentGenerator.startNext())) {
            stage = getNextStage(stage);
            currentGenerator = getNextGenerator();

            if (stage == Stage.SOURCE) {
                reschedule();
                return;
            }
        }
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
            notifyFailed();
        }
    }

    ...

}

从这个函数源码看出,完整执行的情况下,会依次调用ResourceCacheGenerator、DataCacheGenerator和SourceGenerator中的startNext(),接下来,分别来看看三个DataFetcherGenerator的实现类是怎么进行的:

package com.bumptech.glide.load.engine;

...

class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {

    ...

    @Override
    public boolean startNext() {
        List<Key> sourceIds = helper.getCacheKeys();
        if (sourceIds.isEmpty()) {
            return false;
        }
        List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
        while (modelLoaders == null || !hasNextModelLoader()) {
            resourceClassIndex++;
            if (resourceClassIndex >= resourceClasses.size()) {
                sourceIdIndex++;
                if (sourceIdIndex >= sourceIds.size()) {
                    return false;
                }
                resourceClassIndex = 0;
            }

            Key sourceId = sourceIds.get(sourceIdIndex);
            Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
            Transformation<?> transformation = helper.getTransformation(resourceClass);

            currentKey = new ResourceCacheKey(sourceId, helper.getSignature(), helper.getWidth(),
                    helper.getHeight(), transformation, resourceClass, helper.getOptions());
            cacheFile = helper.getDiskCache().get(currentKey);
            if (cacheFile != null) {
                this.sourceKey = sourceId;
                modelLoaders = helper.getModelLoaders(cacheFile);
                modelLoaderIndex = 0;
            }
        }

        ...

    }

    ...

}

很明显,由于是请求一张之前从未被加载过的图片,那么在ROM缓存中是不可能找到对应的缓存文件的,所以最终会因为

if (sourceIdIndex >= sourceIds.size()) {
    return false;
}

而返回false。

package com.bumptech.glide.load.engine;

...

class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {

    ...

    @Override
    public boolean startNext() {
        while (modelLoaders == null || !hasNextModelLoader()) {
            sourceIdIndex++;
            if (sourceIdIndex >= cacheKeys.size()) {
                return false;
            }

            Key sourceId = cacheKeys.get(sourceIdIndex);
            Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
            cacheFile = helper.getDiskCache().get(originalKey);
            if (cacheFile != null) {
                this.sourceKey = sourceId;
                modelLoaders = helper.getModelLoaders(cacheFile);
                modelLoaderIndex = 0;
            }
        }

        ...

    }

    ...

}

这里也需要先取得ROM缓存文件,故和ResourceCacheGenerator的结果一样。

package com.bumptech.glide.load.engine;

...

class SourceGenerator implements DataFetcherGenerator,
        DataFetcher.DataCallback<Object>,
        DataFetcherGenerator.FetcherReadyCallback {

    ...

    @Override
    public boolean startNext() {
        if (dataToCache != null) {
            Object data = dataToCache;
            dataToCache = null;
            cacheData(data);
        }

        if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
            return true;
        }
        sourceCacheGenerator = null;

        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
            loadData = helper.getLoadData().get(loadDataListIndex++);
            if (loadData != null
                    && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
                    || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
                started = true;
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }
        return started;
    }

    ...

}

刚开始执行时,dataToCache必定为null,sourceCacheGenerator也一样,这个时候根据注册的ModelLoader实例创建的LoadData实例的DataFetcher来进行加载远程图片的原始数据,结合注册项可知,执行HttpUrlFetcher中的loadData方法:

package com.bumptech.glide.load.data;

...

public class HttpUrlFetcher implements DataFetcher<InputStream> {

    ...

    @Override
    public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
        long startTime = LogTime.getLogTime();
        final InputStream result;
        try {
            result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
                    glideUrl.getHeaders());
        } catch (IOException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Failed to load data for url", e);
            }
            callback.onLoadFailed(e);
            return;
        }

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
                    + " ms and loaded " + result);
        }
        callback.onDataReady(result);
    }

    ...

}

在这里面通过网络请求得到图片的原始数据的流InputStream(如果请求顺利的话),然后再回调SourceGenerator中的onDataReady方法:

package com.bumptech.glide.load.engine;

...

class SourceGenerator implements DataFetcherGenerator,
        DataFetcher.DataCallback<Object>,
        DataFetcherGenerator.FetcherReadyCallback {

    ...

    @Override
    public void onDataReady(Object data) {
        DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
            dataToCache = data;
            cb.reschedule();
        } else {
            cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
                    loadData.fetcher.getDataSource(), originalKey);
        }
    }

    ...

}

这样一来,就通过dataToCache将图片的原始数据进行临时存储,然后执行reschedule(),即跳转到DecodeJob中去:

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    @Override
    public void reschedule() {
        runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
        callback.reschedule(this);
    }

    ...

}

继续跳转到EngineJob中的reschedule方法中去:

package com.bumptech.glide.load.engine;

...

class EngineJob<R> implements DecodeJob.Callback<R>, Poolable {

    ...

    @Override
    public void reschedule(DecodeJob<?> job) {
        if (isCancelled) {
            MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();
        } else {
            getActiveSourceExecutor().execute(job);
        }
    }

    ...

}

接下来,就是切换线程进行操作,继续从DecodeJob的入口函数进行工作,只是,这次runReason的值换成了SWITCH_TO_SOURCE_SERVICE,运行到runGenerators(),此时的currentGenerator的实例是SourceGenerator,那么执行到其中的startNext()的时候,由于现在的dataToCache已经存放了请求图片的原始数据,所以进行文件缓存:

package com.bumptech.glide.load.engine;

...

class SourceGenerator implements DataFetcherGenerator,
        DataFetcher.DataCallback<Object>,
        DataFetcherGenerator.FetcherReadyCallback {

    ...

    private void cacheData(Object dataToCache) {
        long startTime = LogTime.getLogTime();
        try {
            Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
            DataCacheWriter<Object> writer =
                    new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
            originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
            helper.getDiskCache().put(originalKey, writer);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Finished encoding source to cache"
                        + ", key: " + originalKey
                        + ", data: " + dataToCache
                        + ", encoder: " + encoder
                        + ", duration: " + LogTime.getElapsedMillis(startTime));
            }
        } finally {
            loadData.fetcher.cleanup();
        }

        sourceCacheGenerator =
                new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
    }

    ...

}

在helper.getDiskCache().put(originalKey, writer)中显然是做缓存了:

package com.bumptech.glide.load.engine.cache;

...

public class DiskLruCacheWrapper implements DiskCache {

    ...

    @Override
    public void put(Key key, Writer writer) {
        writeLocker.acquire(key);
        try {
            String safeKey = safeKeyGenerator.getSafeKey(key);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
            }
            try {
                DiskLruCache diskCache = getDiskCache();
                Value current = diskCache.get(safeKey);
                if (current != null) {
                    return;
                }

                DiskLruCache.Editor editor = diskCache.edit(safeKey);
                if (editor == null) {
                    throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
                }
                try {
                    File file = editor.getFile(0);
                    if (writer.write(file)) {
                        editor.commit();
                    }
                } finally {
                    editor.abortUnlessCommitted();
                }
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Unable to put to disk cache", e);
                }
            }
        } finally {
            writeLocker.release(key);
        }
    }

    ...

}

再看writer.write(file)的写入文件操作:

package com.bumptech.glide.load.model;

...

public class StreamEncoder implements Encoder<InputStream> {
    private static final String TAG = "StreamEncoder";
    private final ArrayPool byteArrayPool;

    public StreamEncoder(ArrayPool byteArrayPool) {
        this.byteArrayPool = byteArrayPool;
    }

    @Override
    public boolean encode(InputStream data, File file, Options options) {
        byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
        boolean success = false;
        OutputStream os = null;
        try {
            os = new FileOutputStream(file);
            int read;
            while ((read = data.read(buffer)) != -1) {
                os.write(buffer, 0, read);
            }
            os.close();
            success = true;
        } catch (IOException e) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Failed to encode data onto the OutputStream", e);
            }
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    // Do nothing.
                }
            }
            byteArrayPool.put(buffer, byte[].class);
        }
        return success;
    }
}

注意到,这里是将图片的原始数据流写入到了相应的缓存文件中去,那么特定规格(指定宽高值和色彩模式)的图片缓存文件呢?别急,在SourceGenerator的cacheData方法最后给sourceCacheGenerator创建了一个实例,也就是说,还得执行DataCacheGenerator中的startNext():

package com.bumptech.glide.load.engine;

...

class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {

    ...

    @Override
    public boolean startNext() {
        while (modelLoaders == null || !hasNextModelLoader()) {
            sourceIdIndex++;
            if (sourceIdIndex >= cacheKeys.size()) {
                return false;
            }

            Key sourceId = cacheKeys.get(sourceIdIndex);
            Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
            cacheFile = helper.getDiskCache().get(originalKey);
            if (cacheFile != null) {
                this.sourceKey = sourceId;
                modelLoaders = helper.getModelLoaders(cacheFile);
                modelLoaderIndex = 0;
            }
        }

        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
            ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
            loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(),
                    helper.getHeight(), helper.getOptions());
            if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
                started = true;
                loadData.fetcher.loadData(helper.getPriority(), this);
            }
        }
        return started;
    }

    ...

}

现在肯定能找到图片的缓存文件了,而当缓存文件不为空时,有这样一句:

modelLoaders = helper.getModelLoaders(cacheFile);

那么,根据我前文Glide之旅 —— Registry中获取方法可知,会返回一个包含以下对象的集合:

1. com.bumptech.glide.load.model.ByteBufferFileLoader.Factory#build(MultiModelLoaderFactory)
2. com.bumptech.glide.load.model.FileLoader.StreamFactory#build(MultiModelLoaderFactory)
3. com.bumptech.glide.load.model.FileLoader.FileDescriptorFactory#build(MultiModelLoaderFactory)
4. com.bumptech.glide.load.model.UnitModelLoader.Factory#build(MultiModelLoaderFactory)

依次执行集合中对象的buildLoadData方法,直到满足条件为止。显然,正常情况下,执行第一个ByteBufferFileLoader就能满足条件了,那么这个时候看下执行的源码:

package com.bumptech.glide.load.model;

...

public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {

    ...

    @Override
    public LoadData<ByteBuffer> buildLoadData(File file, int width, int height, Options options) {
        return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
    }

    ...

    private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {

        ...

    }

    ...

}

再来看看LoadData的源码

package com.bumptech.glide.load.model;

...

public interface ModelLoader<Model, Data> {
    class LoadData<Data> {
        public final Key sourceKey;
        public final List<Key> alternateKeys;
        public final DataFetcher<Data> fetcher;

        public LoadData(Key sourceKey, DataFetcher<Data> fetcher) {
            this(sourceKey, Collections.<Key>emptyList(), fetcher);
        }

        public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
            this.sourceKey = Preconditions.checkNotNull(sourceKey);
            this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
            this.fetcher = Preconditions.checkNotNull(fetcher);
        }
    }

    ...

}

那么,下面这个执行流程就疏通了

loadData.fetcher.loadData(helper.getPriority(), this);

直接执行到ByteBufferFetcher#loadData(Priority, DataCallback<? super ByteBuffer>)

package com.bumptech.glide.load.model;

...

public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {

    ...

    private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {

        ...

        @Override
        public void loadData(Priority priority, DataCallback<? super ByteBuffer> callback) {
            ByteBuffer result = null;
            try {
                result = ByteBufferUtil.fromFile(file);
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
                }
                callback.onLoadFailed(e);
                return;
            }

            callback.onDataReady(result);
        }

        ...

    }

    ...

}

执行顺利的话,直接回调DataCacheGenerator#onDataReady(Object),注意到,这里的传参是一个ByteBuffer类型的参数

package com.bumptech.glide.load.engine;

...

class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback<Object> {

    ...

    DataCacheGenerator(List<Key> cacheKeys, DecodeHelper<?> helper, FetcherReadyCallback cb) {
        this.cacheKeys = cacheKeys;
        this.helper = helper;
        this.cb = cb;
    }

    ...

    @Override
    public void onDataReady(Object data) {
        cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
    }

    @Override
    public void onLoadFailed(Exception e) {
        cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
    }
}

至此,DataFetcherGenerator的流程就梳理完了,执行顺利的话,接下来是开始回调DecodeJob#onDataFetcherReady(Key, Object, DataFetcher<?>, DataSource, Key)

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    @Override
    public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
                                   DataSource dataSource, Key attemptedKey) {
        this.currentSourceKey = sourceKey;
        this.currentData = data;
        this.currentFetcher = fetcher;
        this.currentDataSource = dataSource;
        this.currentAttemptingKey = attemptedKey;
        if (Thread.currentThread() != currentThread) {
            runReason = RunReason.DECODE_DATA;
            callback.reschedule(this);
        } else {
            decodeFromRetrievedData();
        }
    }

    ...

    private void decodeFromRetrievedData() {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Retrieved data", startFetchTime,
                    "data: " + currentData
                            + ", cache key: " + currentSourceKey
                            + ", fetcher: " + currentFetcher);
        }
        Resource<R> resource = null;
        try {
            resource = decodeFromData(currentFetcher, currentData, currentDataSource);
        } catch (GlideException e) {
            e.setLoggingDetails(currentAttemptingKey, currentDataSource);
            exceptions.add(e);
        }
        if (resource != null) {
            notifyEncodeAndRelease(resource, currentDataSource);
        } else {
            runGenerators();
        }
    }

    private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
        if (resource instanceof Initializable) {
            ((Initializable) resource).initialize();
        }

        Resource<R> result = resource;
        LockedResource<R> lockedResource = null;
        if (deferredEncodeManager.hasResourceToEncode()) {
            lockedResource = LockedResource.obtain(resource);
            result = lockedResource;
        }

        notifyComplete(result, dataSource);

        stage = Stage.ENCODE;
        try {
            if (deferredEncodeManager.hasResourceToEncode()) {
                deferredEncodeManager.encode(diskCacheProvider, options);
            }
        } finally {
            if (lockedResource != null) {
                lockedResource.unlock();
            }
            onEncodeComplete();
        }
    }

    private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
                                              DataSource dataSource) throws GlideException {
        try {
            if (data == null) {
                return null;
            }
            long startTime = LogTime.getLogTime();
            Resource<R> result = decodeFromFetcher(data, dataSource);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Decoded result " + result, startTime);
            }
            return result;
        } finally {
            fetcher.cleanup();
        }
    }

    private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
            throws GlideException {
        LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
        return runLoadPath(data, dataSource, path);
    }

    private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
                                                         LoadPath<Data, ResourceType, R> path) throws GlideException {
        DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
        try {
            return path.load(rewinder, options, width, height,
                    new DecodeCallback<ResourceType>(dataSource));
        } finally {
            rewinder.cleanup();
        }
    }

    ...

}

当执行到decodeFromFetcher(Data, DataSource)的内部时,需要根据ByteBuffer类型返回一个LoadPath的对象作为传参,关于这个传参的获取,在我的前文Glide之旅 —— Registry中进行了详细地梳理,而rewinder则是一个ByteBufferRewinder的实例,接下来执行com.bumptech.glide.load.engine.LoadPath#load(DataRewinder<Data>, Options, int, int, DecodePath.DecodeCallback<ResourceType>)

package com.bumptech.glide.load.engine;

...

public class LoadPath<Data, ResourceType, Transcode> {

    ...

    public Resource<Transcode> load(DataRewinder<Data> rewinder, Options options, int width,
                                    int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
        List<Exception> exceptions = listPool.acquire();
        try {
            return loadWithExceptionList(rewinder, options, width, height, decodeCallback, exceptions);
        } finally {
            listPool.release(exceptions);
        }
    }

    private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder, Options options,
                                                      int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
                                                      List<Exception> exceptions) throws GlideException {
        int size = decodePaths.size();
        Resource<Transcode> result = null;
        for (int i = 0; i < size; i++) {
            DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
            try {
                result = path.decode(rewinder, width, height, options, decodeCallback);
            } catch (GlideException e) {
                exceptions.add(e);
            }
            if (result != null) {
                break;
            }
        }

        if (result == null) {
            throw new GlideException(failureMessage, new ArrayList<>(exceptions));
        }

        return result;
    }

    ...

}

可以看到,主要还是要执行com.bumptech.glide.load.engine.DecodePath#decode(DataRewinder<DataType>, int, int, Options, DecodeCallback<ResourceType>)

package com.bumptech.glide.load.engine;

...

public class DecodePath<DataType, ResourceType, Transcode> {

    ...

    public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
                                      Options options, DecodeCallback<ResourceType> callback) throws GlideException {
        Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
        Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
        return transcoder.transcode(transformed);
    }

    private Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,
                                                  int height, Options options) throws GlideException {
        List<Exception> exceptions = listPool.acquire();
        try {
            return decodeResourceWithList(rewinder, width, height, options, exceptions);
        } finally {
            listPool.release(exceptions);
        }
    }

    private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
                                                          int height, Options options, List<Exception> exceptions) throws GlideException {
        Resource<ResourceType> result = null;
        for (int i = 0, size = decoders.size(); i < size; i++) {
            ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
            try {
                DataType data = rewinder.rewindAndGet();
                if (decoder.handles(data, options)) {
                    data = rewinder.rewindAndGet();
                    result = decoder.decode(data, width, height, options);
                }
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Failed to decode data for " + decoder, e);
                }
                exceptions.add(e);
            }

            if (result != null) {
                break;
            }
        }

        if (result == null) {
            throw new GlideException(failureMessage, new ArrayList<>(exceptions));
        }
        return result;
    }

    ...

}

在decodeResourceWithList方法中decoders就是我前文中得出来的

1. new DecodePath(ByteBuffer, GifDrawable, Drawable, [ByteBufferGifDecoder], UnitTranscoder, exceptionListPool)
2. new DecodePath(ByteBuffer, Bitmap, BitmapDrawable, [ByteBufferBitmapDecoder], BitmapDrawableTranscoder, exceptionListPool)
3. new DecodePath(ByteBuffer, BitmapDrawable, Drawable, [BitmapDrawableDecoder], UnitTranscoder, exceptionListPool)

其中一个DecodePath实例的第四个用中括号括起来的参数,表示一个集合,那么也就是说,直到在上面三个DecodePath实例的decoders中找到可以执行的对象

package com.bumptech.glide.load.resource.gif;

...

public class ByteBufferGifDecoder implements ResourceDecoder<ByteBuffer, GifDrawable> {

    ...

    @Override
    public boolean handles(ByteBuffer source, Options options) throws IOException {
        return !options.get(DISABLE_ANIMATION)
                && ImageHeaderParserUtils.getType(parsers, source) == ImageHeaderParser.ImageType.GIF;
    }

    ...

}

只要远程图片不是GIF类型的,那么这个对象肯定就不能继续执行

package com.bumptech.glide.load.resource.bitmap;

...

public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
    private final Downsampler downsampler;

    public ByteBufferBitmapDecoder(Downsampler downsampler) {
        this.downsampler = downsampler;
    }

    @Override
    public boolean handles(ByteBuffer source, Options options) throws IOException {
        return downsampler.handles(source);
    }

    @Override
    public Resource<Bitmap> decode(ByteBuffer source, int width, int height, Options options)
            throws IOException {
        InputStream is = ByteBufferUtil.toStream(source);
        return downsampler.decode(is, width, height, options);
    }
}

再附上Dwonsaampler的源码

package com.bumptech.glide.load.resource.bitmap;

...

public final class Downsampler {

    ...

    public boolean handles(ByteBuffer byteBuffer) {
        // We expect downsampler to handle any available type Android supports.
        return true;
    }

    public Resource<Bitmap> decode(InputStream is, int outWidth, int outHeight,
                                   Options options) throws IOException {
        return decode(is, outWidth, outHeight, options, EMPTY_CALLBACKS);
    }

    public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
                                   Options options, DecodeCallbacks callbacks) throws IOException {
        Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
                + " mark()");

        byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
        BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
        bitmapFactoryOptions.inTempStorage = bytesForOptions;

        DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
        DownsampleStrategy downsampleStrategy = options.get(DOWNSAMPLE_STRATEGY);
        boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);

        try {
            Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
                    downsampleStrategy, decodeFormat, requestedWidth, requestedHeight,
                    fixBitmapToRequestedDimensions, callbacks);
            return BitmapResource.obtain(result, bitmapPool);
        } finally {
            releaseOptions(bitmapFactoryOptions);
            byteArrayPool.put(bytesForOptions, byte[].class);
        }
    }

    ...

}

显然,这里就返回了我们特定规格的图片了!而返回类型为Resource<Bitmap>,也就不再用到BitmapDrawableDecoder了。接下来回头看DecodePath

package com.bumptech.glide.load.engine;

...

public class DecodePath<DataType, ResourceType, Transcode> {

    ...

    public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
                                      Options options, DecodeCallback<ResourceType> callback) throws GlideException {
        Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
        Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
        return transcoder.transcode(transformed);
    }

    ...

}

回调com.bumptech.glide.load.engine.DecodeJob.DecodeCallback#onResourceDecoded(Resource<Z>)

package com.bumptech.glide.load.engine;

...

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable, Comparable<DecodeJob<?>>, Poolable {

    ...

    private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {
        private final DataSource dataSource;

        @Synthetic
        DecodeCallback(DataSource dataSource) {
            this.dataSource = dataSource;
        }

        @Override
        public Resource<Z> onResourceDecoded(Resource<Z> decoded) {
            Class<Z> resourceSubClass = getResourceClass(decoded);
            Transformation<Z> appliedTransformation = null;
            Resource<Z> transformed = decoded;
            if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
                appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
                transformed = appliedTransformation.transform(decoded, width, height);
            }
            // TODO: Make this the responsibility of the Transformation.
            if (!decoded.equals(transformed)) {
                decoded.recycle();
            }

            final EncodeStrategy encodeStrategy;
            final ResourceEncoder<Z> encoder;
            if (decodeHelper.isResourceEncoderAvailable(transformed)) {
                encoder = decodeHelper.getResultEncoder(transformed);
                encodeStrategy = encoder.getEncodeStrategy(options);
            } else {
                encoder = null;
                encodeStrategy = EncodeStrategy.NONE;
            }

            Resource<Z> result = transformed;
            boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
            if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
                    encodeStrategy)) {
                if (encoder == null) {
                    throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
                }
                final Key key;
                if (encodeStrategy == EncodeStrategy.SOURCE) {
                    key = new DataCacheKey(currentSourceKey, signature);
                } else if (encodeStrategy == EncodeStrategy.TRANSFORMED) {
                    key = new ResourceCacheKey(currentSourceKey, signature, width, height,
                            appliedTransformation, resourceSubClass, options);
                } else {
                    throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
                }

                LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
                deferredEncodeManager.init(key, encoder, lockedResult);
                result = lockedResult;
            }
            return result;
        }

        @SuppressWarnings("unchecked")
        private Class<Z> getResourceClass(Resource<Z> resource) {
            return (Class<Z>) resource.get().getClass();
        }
    }

    ...

}

从上面的梳理结果可知,上面的泛型Z为Bitmap类型。

see you again

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值