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

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

作者: 两行哥 | 来源:发表于2018-08-05 16:17 被阅读41次

    前文分析了Glide的with()、load()以及into()方法中的大部分逻辑,今天我们来分析Glide的核心图片加载逻辑。因为Glide的封装程度比较高,之前三篇文章分析完毕其外围逻辑(参阅:〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(一)〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(二)〔两行哥〕提纲挈领,带你梳理Glide主要源码逻辑(三))。
    那么Glide图片加载的核心逻辑在哪儿呢?就是上文结尾所说的engine.load()方法,在engine.load()方法中传入了上文已经分析过的DataFetcher实例、LoadProvider实例以及ResourceTransCoder实例。让我们进入源码分析看看。

    Engine.java
        /**
         * Starts a load for the given arguments. Must be called on the main thread.
         *
         * <p>
         *     The flow for any request is as follows:
         *     <ul>
         *         <li>Check the memory cache and provide the cached resource if present</li>
         *         <li>Check the current set of actively used resources and return the active resource if present</li>
         *         <li>Check the current set of in progress loads and add the cb to the in progress load if present</li>
         *         <li>Start a new load</li>
         *     </ul>
         * </p>
         *
         * <p>
         *     Active resources are those that have been provided to at least one request and have not yet been released.
         *     Once all consumers of a resource have released that resource, the resource then goes to cache. If the
         *     resource is ever returned to a new consumer from cache, it is re-added to the active resources. If the
         *     resource is evicted from the cache, its resources are recycled and re-used if possible and the resource is
         *     discarded. There is no strict requirement that consumers release their resources so active resources are
         *     held weakly.
         * </p>
         *
         * @param signature A non-null unique key to be mixed into the cache key that identifies the version of the data to
         *                  be loaded.
         * @param width The target width in pixels of the desired resource.
         * @param height The target height in pixels of the desired resource.
         * @param fetcher The fetcher to use to retrieve data not in the disk cache.
         * @param loadProvider The load provider containing various encoders and decoders use to decode and encode data.
         * @param transformation The transformation to use to transform the decoded resource.
         * @param transcoder The transcoder to use to transcode the decoded and transformed resource.
         * @param priority The priority with which the request should run.
         * @param isMemoryCacheable True if the transcoded resource can be cached in memory.
         * @param diskCacheStrategy The strategy to use that determines what type of data, if any,
         *                          will be cached in the local disk cache.
         * @param cb The callback that will be called when the load completes.
         *
         * @param <T> The type of data the resource will be decoded from.
         * @param <Z> The type of the resource that will be decoded.
         * @param <R> The type of the resource that will be transcoded from the decoded resource.
         */
        public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
                DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
                Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
            Util.assertMainThread();
            long startTime = LogTime.getLogTime();
    
            final String id = fetcher.getId();
            EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                    loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                    transcoder, loadProvider.getSourceEncoder());
    
            EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
            if (cached != null) {
                cb.onResourceReady(cached);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Loaded resource from cache", startTime, key);
                }
                return null;
            }
    
            EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
            if (active != null) {
                cb.onResourceReady(active);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Loaded resource from active resources", startTime, key);
                }
                return null;
            }
    
            EngineJob current = jobs.get(key);
            if (current != null) {
                current.addCallback(cb);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Added to existing load", startTime, key);
                }
                return new LoadStatus(cb, current);
            }
    
            EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
            DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                    transcoder, diskCacheProvider, diskCacheStrategy, priority);
            EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
            jobs.put(key, engineJob);
            engineJob.addCallback(cb);
            engineJob.start(runnable);
    
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Started new load", startTime, key);
            }
            return new LoadStatus(cb, engineJob);
        }
    
        private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
            if (!isMemoryCacheable) {
                return null;
            }
    
            EngineResource<?> active = null;
            WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
            if (activeRef != null) {
                active = activeRef.get();
                if (active != null) {
                    active.acquire();
                } else {
                    activeResources.remove(key);
                }
            }
    
            return active;
        }
    
        private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
            if (!isMemoryCacheable) {
                return null;
            }
    
            EngineResource<?> cached = getEngineResourceFromCache(key);
            if (cached != null) {
                cached.acquire();
                activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
            }
            return cached;
        }
    

    首先先做一下知识的铺垫。我们知道Glide中有三级缓存机制,拥有内存缓存和硬盘缓存。而内存缓存同时采用了两种机制,一种是前文讲过的LruCache算法机制(什么是LruCache算法?请参阅前文),另外一种就是弱引用机制。缓存的作用是什么呢?内存缓存的作用就是防止把图片重复加载到内存中,而硬盘缓存的作用就是防止重复从网络中获取图片。

    一、内存缓存

    让我们回到load()方法中。首先调用了fetcher.getId()获得了一个唯一标识Id。这个Id是图片加载的唯一标识Id(如果是网络图片加载请求,那么其实就是图片的Url)。接着通过EngineKey的构建者模式,传入了获取到的Id、Width、Height、transformation等参数,构建了另外一个标识Key。这个Key是什么呢?上文讲到Glide的内存缓存,这个Key就是内存缓存的唯一标识Key,与传入的Id、Width、Height、transformation参数相关。为什么要与这么多参数相关呢?大家想一下,需要加载的图片只有与内存缓存中的某图片Url、宽高、解码方式等一致,才可以直接取用内存缓存。如果需要加载的图片改变了Url或者宽高或者解码方式,那么原有旧的内存缓存就不能使用了,需要重新缓存。那么在EngineKey的构建方法中传入如此多的参数也可以理解了。
    接着往下看。上文说过Glide的内存缓存同时使用了两种缓存机制,一种是LruCache缓存机制,一种是弱引用缓存机制,让我们一一分析。首先,load()方法内部调用了loadFromCache()方法,源码如下:

    Engine.java
        private final MemoryCache cache;
        private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
    
        private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
            if (!isMemoryCacheable) {
                return null;
            }
    
            EngineResource<?> cached = getEngineResourceFromCache(key);
            if (cached != null) {
                cached.acquire();
                activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
            }
            return cached;
        }
    
        @SuppressWarnings("unchecked")
        private EngineResource<?> getEngineResourceFromCache(Key key) {
            Resource<?> cached = cache.remove(key);
    
            final EngineResource result;
            if (cached == null) {
                result = null;
            } else if (cached instanceof EngineResource) {
                // Save an object allocation if we've cached an EngineResource (the typical case).
                result = (EngineResource) cached;
            } else {
                result = new EngineResource(cached, true /*isCacheable*/);
            }
            return result;
        }
    

    可以看到逻辑是从cache中获取到缓存(cache中存储的是曾经使用过,而当前没有在使用中的缓存。通过调用cache.remove(key)方法删除缓存并返回被删除的缓存),如果获取到的缓存result不为null,调用 activeResources.put()方法将此缓存放入激活缓存Map中(正在使用中的缓存)。而这里的cache为实现了MemoryCache接口的LruResourceCache类的实例。我们稍微看一下LruResourceCache类的源码:

    LruResourceCache.java
    public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
        private ResourceRemovedListener listener;
    
        /**
         * Constructor for LruResourceCache.
         *
         * @param size The maximum size in bytes the in memory cache can use.
         */
        public LruResourceCache(int size) {
            super(size);
        }
    
        @Override
        public void setResourceRemovedListener(ResourceRemovedListener listener) {
            this.listener = listener;
        }
    
        @Override
        protected void onItemEvicted(Key key, Resource<?> item) {
            if (listener != null) {
                listener.onResourceRemoved(item);
            }
        }
    
        @Override
        protected int getSize(Resource<?> item) {
            return item.getSize();
        }
    
        @SuppressLint("InlinedApi")
        @Override
        public void trimMemory(int level) {
            if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
                // Nearing middle of list of cached background apps
                // Evict our entire bitmap cache
                clearMemory();
            } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                // Entering list of cached background apps
                // Evict oldest half of our bitmap cache
                trimToSize(getCurrentSize() / 2);
            }
        }
    }
    

    留意一下isMemoryCacheable变量,这个布尔值是在配置Glide参数的时候设置的,决定了Glide是否使用内存缓存。如果为false,这里获取内存缓存的方法就会返回false。LruResourceCache类就不再赘述,之前的文章就已经分析过LruCache,只要知道cache是基于LruCache算法,内部是基于LinkedHashMap实现的。
    回到load()方法中,如果loadFromCache()方法返回的cached对象不为null,则执行onResourceReady()回调。如果获取到的cached为null(没有从LruCache中的LinkedHashMap中获取到缓存),则调用loadFromActiveResources()方法,再去激活缓存的Map中(使用中的缓存)去寻找是否有缓存,看看内部源码的逻辑:

    Engine.java
        private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
        private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
            if (!isMemoryCacheable) {
                return null;
            }
    
            EngineResource<?> active = null;
            WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
            if (activeRef != null) {
                active = activeRef.get();
                if (active != null) {
                    active.acquire();
                } else {
                    activeResources.remove(key);
                }
            }
    
            return active;
        }
    

    activeResources是持有了缓存弱引用的HashMap,而这里的逻辑主要是从activeResources这个HashMap中获取到图片的缓存。如果调用loadFromActiveResources()方法返回的缓存不为null,同样执行onResourceReady()回调方法,如果返回的缓存为null,则进入下一步,从磁盘中或者网络去获取图片。

    二、磁盘缓存及图片获取

    Glide磁盘缓存与内存缓存类似,也采用了LruCache算法。首先让我们追踪一下Glide读取磁盘缓存的逻辑。
    在读取内存缓存之后,如果没有获取到内存缓存,Glide就会去读取磁盘缓存,如下代码:

    EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
    EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
    jobs.put(key, engineJob);
    engineJob.addCallback(cb);
    engineJob.start(runnable);
    

    首先创建了一个EngineJob并放入jobs集合,然后engineJob开始执行传入的EngineRunnable任务。本地磁盘缓存的逻辑就隐藏在EngineRunnable中,让我们看看这个Runnable对象中覆写的run()方法:

    EngineRunnable.java
        @Override
        public void run() {
            if (isCancelled) {
                return;
            }
    
            Exception exception = null;
            Resource<?> resource = null;
            try {
                resource = decode();
            } catch (OutOfMemoryError e) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Out Of Memory Error decoding", e);
                }
                exception = new ErrorWrappingGlideException(e);
            } 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);
            }
        }
    

    可以看到run()方法中执行了decode()方法,并在执行结束后根据执行的结果,调用了onLoadFailed()或者onLoadComplete()回调。再让我们看看decode()方法到底做了哪些工作。

    EngineRunnable.java
        private Resource<?> decode() throws Exception {
            ...
            private boolean isDecodingFromCache() {
                return stage == Stage.CACHE;
            }
            ...
            if (isDecodingFromCache()) {
                return decodeFromCache();
            } else {
                return decodeFromSource();
            }
            ...
        }
    

    decode()方法根据isDecodingFromCache()方法的返回值(这个参数在初始化Glile的时候进行过配置,是否使用磁盘缓存),分别调用decodeFromCache()和decodeFromSource()。磁盘缓存的逻辑终于被找到了,就在decodeFromCache()中!

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

    可以看到decodeFromCache()方法中执行了decodeResultFromCache()或decodeSourceFromCache()方法。首先调用decodeResultFromCache()方法获取缓存,如果获取不到,就调用decodeSourceFromCache()方法获取缓存。至于什么时候执行第一个方法,什么时候又会执行到第二个方法,与我们在第一篇中讲的磁盘缓存配置策略有关:

    diskCacheStrategy(DiskCacheStrategy.RESULT)//缓存处理后的图像(如尺寸调整、裁剪后的图像)
    diskCacheStrategy(DiskCacheStrategy.SOURCE)//缓存原尺寸的图像
    diskCacheStrategy(DiskCacheStrategy.ALL)//缓存所有图像
    

    见名知意,两行哥就不再深入讲解了,让我们看一下源码:

    DecodeJob.java
         public Resource<Z> decodeResultFromCache() throws Exception {
            if (!diskCacheStrategy.cacheResult()) {
                return null;
            }
    
            long startTime = LogTime.getLogTime();
            Resource<T> transformed = loadFromCache(resultKey);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Decoded transformed from cache", startTime);
            }
            startTime = LogTime.getLogTime();
            Resource<Z> result = transcode(transformed);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transcoded transformed from cache", startTime);
            }
            return result;
        }
    
        public Resource<Z> decodeSourceFromCache() throws Exception {
            if (!diskCacheStrategy.cacheSource()) {
                return null;
            }
    
            long startTime = LogTime.getLogTime();
            Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Decoded source from cache", startTime);
            }
            return transformEncodeAndTranscode(decoded);
        }
    

    可以看到,它们内部都调用了loadFromCache()方法从缓存当中获取数据,如果是decodeResultFromCache()方法就直接将数据解码并返回,如果是decodeSourceFromCache()方法,还要调用一下transformEncodeAndTranscode()方法转换数据后再解码并返回。
    再者,它们调用loadFromCache()方法时传入的参数却不一样,一个传入的是resultKey,另外一个却又调用了resultKey的getOriginalKey()方法,分别对应处理后图片缓存Key和原图缓存Key。上文讲过,Glide的缓存Key是由多个参数共同组成的,包括图片的width、height等。但如果我们如果缓存的原始图片,其实并不需要这么多的参数,因为并没有对原始图片进行过尺寸缩放等额外处理。那么我们来看一下getOriginalKey()方法的源码:

    OriginalKey.java
        public Key getOriginalKey() {
            if (originalKey == null) {
                originalKey = new OriginalKey(id, signature);
            }
            return originalKey;
        }
    

    可以看到,这里忽略了大部分的参数,只使用了id和signature两个参数来构成Key,而signature参数大多数情况下都是用不到的,因此几乎是由id(也就是图片url)来决定OriginalKey。
    接下来让我们回到loadFromCache()方法,看看内部的源码:

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

    loadFromCache()方法调用getDiskCache()方法获取到的就是Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法并把缓存key传入,得到硬盘缓存的文件。如果文件为null就返回null,如果文件不为空则将它解码成Resource对象后返回。
    让我们重新回到decode()方法,看看方法中另一个else分支decodeFromSource()方法执行了什么逻辑:
    DecodeJob.java

        public Resource<Z> decodeFromSource() throws Exception {
            Resource<T> decoded = decodeSource();
            return transformEncodeAndTranscode(decoded);
        }
    
        private Resource<T> decodeSource() throws Exception {
            Resource<T> decoded = null;
            try {
                long startTime = LogTime.getLogTime();
                final A data = fetcher.loadData(priority);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Fetched data", startTime);
                }
                if (isCancelled) {
                    return null;
                }
                decoded = decodeFromSourceData(data);
            } finally {
                fetcher.cleanup();
            }
            return decoded;
        }
    
        private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
            long startTime = LogTime.getLogTime();
            Resource<T> transformed = transform(decoded);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transformed resource from source", startTime);
            }
    
            writeTransformedToCache(transformed);
    
            startTime = LogTime.getLogTime();
            Resource<Z> result = transcode(transformed);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transcoded transformed from source", startTime);
            }
            return result;
        }
    

    decodeFromSource()中只有两行代码,decodeSource()顾名思义是用来解析原图片的,而transformEncodeAndTranscode()则是用来对图片进行转换和转码的。decodeSource()方法中主要做了如下逻辑:
    1.通过 fetcher.loadData(priority)方法来获取到图片数据,比如从网络获取图片资源;
    2.通过decodeFromSourceData(data)方法来解码数据:
    3.decodeFromSourceData()方法中,判断配置的缓存策略是否支持磁盘缓存,如果支持的话,则调用cacheAndDecodeSourceData()方法来缓存数据及解码。Glide提供了DiskCacheProvider类,其本质是封装后的DiskLruCache实例。
    相关代码如下:
    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());
            }
        }
    

    DecodeJob.java

        private Resource<T> decodeFromSourceData(A data) throws IOException {
            final Resource<T> decoded;
            if (diskCacheStrategy.cacheSource()) {
                decoded = cacheAndDecodeSourceData(data);
            } else {
                long startTime = LogTime.getLogTime();
                decoded = loadProvider.getSourceDecoder().decode(data, width, height);
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    logWithTimeAndKey("Decoded from source", startTime);
                }
            }
            return decoded;
        }
    
        private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
            long startTime = LogTime.getLogTime();
            SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
            diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Wrote source to cache", startTime);
            }
    
            startTime = LogTime.getLogTime();
            Resource<T> result = loadFromCache(resultKey.getOriginalKey());
            if (Log.isLoggable(TAG, Log.VERBOSE) && result != null) {
                logWithTimeAndKey("Decoded source from cache", startTime);
            }
            return result;
        }
    

    注意,diskCacheProvider调用了getDiskCache()方法获取DiskLruCache实例,接着调用它的put()方法写入硬盘缓存,这里原始图片的缓存key用的是resultKey.getOriginalKey(),最后让我们再看一下decodeFromSource()方法中第二行transformEncodeAndTranscode(decoded)方法内部做了哪些逻辑:
    DecodeJob.java

        private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
            long startTime = LogTime.getLogTime();
            Resource<T> transformed = transform(decoded);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transformed resource from source", startTime);
            }
    
            writeTransformedToCache(transformed);
    
            startTime = LogTime.getLogTime();
            Resource<Z> result = transcode(transformed);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Transcoded transformed from source", startTime);
            }
            return result;
        }
    
        private void writeTransformedToCache(Resource<T> transformed) {
            if (transformed == null || !diskCacheStrategy.cacheResult()) {
                return;
            }
            long startTime = LogTime.getLogTime();
            SourceWriter<Resource<T>> writer = new SourceWriter<Resource<T>>(loadProvider.getEncoder(), transformed);
            diskCacheProvider.getDiskCache().put(resultKey, writer);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Wrote transformed from source to cache", startTime);
            }
        }
    

    transformEncodeAndTranscode()方法首先调用transform()方法来对图片进行转换,然后在writeTransformedToCache()方法中将转换过后的图片写入到硬盘缓存中,调用的同样是DiskLruCache实例的put()方法,不过这里用的缓存key是resultKey。

    至此,我们已经把Glide内部主要逻辑分析完毕,可以说Glide是Android现有开源框架出色封装的典范,封装后用户完全感觉不到内部复杂的逻辑,仅仅通过一行链式调用就完成了很多逻辑。不得不惊叹作者的高超代码艺术。让我们下次再会。

    相关文章

      网友评论

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

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