源码分析--Glide源码 三 Glide的缓存

写在前面

对于一个应用来讲所需要的图片不可能总是来自他自己的apk包中,总会有一些实时的图片来自于网络、服务器中,而为了流量、加载速度等方面的考虑,我们做不到每一次都是从网络中下载,为了解决这个问题,我们提出了缓存这个概念。
如果你发现本文中有任何错误,请在评论区留言或者私信我,我会第一时间改正,谢谢!

0 图片的三级缓存

对于图片来讲,缓存是十分有必要的,在Android的发展史中,图片的缓存慢慢分成了三级。
内存缓存 本地缓存 网络
相对具体的可以参看这篇文章:三级缓存

1 Glide中的disk缓存

为什么先要将disk缓存,而不是内存缓存呢?
因为disk缓存,是紧挨着网络的,而内存缓存不是紧挨网络的,内存缓存与网络中间还隔了一层disk缓存。由于已经初步了解Glide的加载过程之后,先去了解disk缓存远远比了解内存缓存要容易。
在Glide中,disk缓存的获取是在EngineRunning中,在上一篇博文中,我们可以知道第一次执行EngineRunning的状态都是Stage.CACHE,即从缓存中获取,看一下具体的实现

//EngineRunning
private Resource<?> decode() throws Exception {
    
    
    //不设置的话这个判断第一次是会放回true
    if (isDecodingFromCache()) {
    
    
        return decodeFromCache();
    } else {
    
    
        return decodeFromSource();
    }
}

看一下decodeFromCache的实现

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

//decodeJob.java
/**
 * Returns a transcoded resource decoded from transformed resource data in the disk cache, or null if no such
 * resource exists.
 *
 * @throws Exception
 */
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;
}

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

不难看出这一层是使用diskLruCache来获取缓存的,再看下去

//EngineRunnable.java
private Resource<?> decodeFromCache() throws Exception {
    
    
    ...
    if (result == null) {
    
    
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

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

乍一看,这更前面获取disk缓存没什么差别,但是其实这里是做了小区分的。
在Glide中,对应缓存key的类是EngineKey,而这个EngineKey的构成是由很多因素构成的,至于会被什么因素影响,可以通过EngineKey的构造函数粗略的知晓。
而在这里,这两个获取disk缓存的key是不同地,第一次获取用的是完整的EngineKey,即是带有长、宽等约束条件的EngineKey,获取出来之后可以直接复用。
而第二次获取用的key带的约束条件只有id和signature,即没有加工过的原图片。如果这个原图片可以获取得到的话,需要后续加工即transformEncodeAndTranscode方法后,才能使用。
再看一下transformEncodeAndTranscode的具体实现

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

可以看到writeTransformedToCache又把图片资源文件(带有长宽的)放入disk缓存中,至此disk缓存中就有两张照片了,他们一张是原图,一张是加工后的图片。
这以上都是取得逻辑,再看一下存的逻辑,当下载完成图片之后,会把图片存到disk缓存中,逻辑还在decodeJob类中,关注一下

//decodeJob
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<T> decodeFromSourceData(A data) throws IOException {
    
    
        final Resource<T> decoded;
        //判断是否要disk缓存
        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;
    }

下载好的图片首先会经过decodeFromSourceData方法,然后进入cacheAndDecodeSourceData这个方法是存原图的。后续的话就会过一遍transformEncodeAndTranscode这个方法(存带有长宽的图片)
到这里disk缓存也就讲完了。

2 Glide中的内存缓存

相比于disk缓存,内存缓存的相应速度要快上许多,我们最期望的图片获取方式是从内存中获取,看一下Glide是怎么获取的吧
在Glide中,内存获取的方式是在Engine中,看一下具体的实现

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

可以看到这里先创建了一个EngineKey,然后再调用loadFromCache方法,看一下这个方法的实现

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

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

这里先判断是否需要使用内存缓存,不需要直接return null,接着调用了getEngineResourceFromCache这个方法。getEngineResourceFromCache这个方法尝试从cache中获取图片,cache是在with过程中就被初始话的参数

//GlideBuilder.java
    	if (memoryCache == null) {
    
    
            memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
        }

        if (diskCacheFactory == null) {
    
    
            diskCacheFactory = new InternalCacheDiskCacheFactory(context);
        }

看一下这个LruResourceCache类,它是继承了LruCache的,显然他就是属于内存缓存的。那么getEngineResourceFromCache的作用就是从lru中获取出来对应的图片并吧图片转化成EngineResource类。回到loadFromCache方法,得到图片之后他做了一个很有意思的操作。

  if (cached != null) {
    
    
            cached.acquire();
            activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
        }

看一下cached.acquire()这个的实现

//EngineResource.java
private int acquired;

 void acquire() {
    
    
        if (isRecycled) {
    
    
            throw new IllegalStateException("Cannot acquire a recycled resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
    
    
            throw new IllegalThreadStateException("Must call acquire on the main thread");
        }
        ++acquired;
    }

 void release() {
    
    
        if (acquired <= 0) {
    
    
            throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
    
    
            throw new IllegalThreadStateException("Must call release on the main thread");
        }
        if (--acquired == 0) {
    
    
            listener.onResourceReleased(key, this);
        }
    }

这个方法将EngineResource的acquired+1,与之对应的是在release的时候-1,还可以看到release的时候还多了一个判断,并且当acquired==0的时候调用了 listener.onResourceReleased方法。这个listener我们先暂时放一下,这里还不能确定他指的是什么。
cached.acquire();之后,他还调用了activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));方法,activeResources是一个HashMap<Key, WeakReference<EngineResource<?>>>()。用于存储从memoryCache中remove出来的值。再回到load方法里面

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

如果从loadFromCache从LruCache中或取出来图片,直接通过回调返回出去。再看下去

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

又调用了loadFromActiveResources方法获取,看一下具体的实现

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中通过key查找缓存,如果有就返回,没有就返回null
看完了获取内存缓存,再看一下怎么放到内存缓存中的。根据图片的三级缓存规则来讲,放到内存缓存应该是从网络下载完图片之后,在顺便放到disk和内存中的。具体的代码是从EngineJob中开始的

private void handleResultOnMainThread() {
    
    
        if (isCancelled) {
    
    
            resource.recycle();
            return;
        } else if (cbs.isEmpty()) {
    
    ··········
            throw new IllegalStateException("Received a resource without any callbacks to notify");
        }
        //关注以下部分代码
        engineResource = engineResourceFactory.build(resource, isCacheable);
        hasResource = true;

        // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
        // synchronously released by one of the callbacks.
        engineResource.acquire();
        listener.onEngineJobComplete(key, engineResource);

        for (ResourceCallback cb : cbs) {
    
    
            if (!isInIgnoredCallbacks(cb)) {
    
    
                engineResource.acquire();
                cb.onResourceReady(engineResource);
            }
        }
        // Our request is complete, so we can release the resource.
        engineResource.release();
    }

首先,根据资源创建相应的EngineResource,然后调用了我们之前调用的acquire方法,注意一下,每有一个callback都会调用一次acquire.再看一下listener.onEngineJobComplete(key, engineResource)方法

//Engine.java
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
    
    
        Util.assertMainThread();
        // A null resource indicates that the load failed, usually due to an exception.
        if (resource != null) {
    
    
            resource.setResourceListener(key, this);

            if (resource.isCacheable()) {
    
    
                activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
            }
        }
        // TODO: should this check that the engine job is still current?
        jobs.remove(key);
    }

这个方法里给resource设置了Listener。那么看一下这个listener干了什么吧

//EngineResource.java
    void release() {
    
    
        if (acquired <= 0) {
    
    
            throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
    
    
            throw new IllegalThreadStateException("Must call release on the main thread");
        }
        if (--acquired == 0) {
    
    
            listener.onResourceReleased(key, this);
        }
    }

//Engine.java
    @Override
    public void onResourceReleased(Key cacheKey, EngineResource resource) {
    
    
        Util.assertMainThread();
        activeResources.remove(cacheKey);
        if (resource.isCacheable()) {
    
    
            cache.put(cacheKey, resource);
        } else {
    
    
            resourceRecycler.recycle(resource);
        }
    }

这个Listener在acquired =0的时候,会把activeResources中的图片放回到LruCache中。
结合以上的总结,可以知道acquired是用来表示这个图片资源有多少正在被使用的。当这个值被清空的时候,会把弱引用中的图片放回LruCache中。
总的来讲,Glide缓存相关的逻辑也就讲完了。这里整个缓存的逻辑是很完善,非要鸡蛋里挑骨头的话,只有在内存缓存中获取的逻辑顺序应该调换一下,应该先从hashmap中获取,然后在从LruCache中获取,而在Glide的后续版本(4.0.10)上面,这里也确实被调整过来了。

/**
     * Starts a load for the given arguments.
     *
     * <p>Must be called on the main thread.
     *
     * <p>The flow for any request is as follows:
     *
     * <ul>
     * <li>Check the current set of actively used resources, return the active resource if present,
     * and move any newly inactive resources into the memory cache.
     * <li>Check the memory cache and provide the cached resource if present.
     * <li>Check the current set of in progress loads and add the cb to the in progress load if one
     * is present.
     * <li>Start a new load.
     * </ul>
     *
     * <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.
     *
     * @param width  The target width in pixels of the desired resource.
     * @param height The target height in pixels of the desired resource.
     * @param cb     The callback that will be called when the load completes.
     */
    public <R> LoadStatus load(
            GlideContext glideContext,
            Object model,
            Key signature,
            int width,
            int height,
            Class<?> resourceClass,
            Class<R> transcodeClass,
            Priority priority,
            DiskCacheStrategy diskCacheStrategy,
            Map<Class<?>, Transformation<?>> transformations,
            boolean isTransformationRequired,
            boolean isScaleOnlyOrNoTransform,
            Options options,
            boolean isMemoryCacheable,
            boolean useUnlimitedSourceExecutorPool,
            boolean useAnimationPool,
            boolean onlyRetrieveFromCache,
            ResourceCallback cb,
            Executor callbackExecutor) {
    
    
        long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

        EngineKey key =
                keyFactory.buildKey(
                        model,
                        signature,
                        width,
                        height,
                        transformations,
                        resourceClass,
                        transcodeClass,
                        options);

        EngineResource<?> memoryResource;
        synchronized (this) {
    
    
            memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

            if (memoryResource == null) {
    
    
                return waitForExistingOrStartNewJob(
                        glideContext,
                        model,
                        signature,
                        width,
                        height,
                        resourceClass,
                        transcodeClass,
                        priority,
                        diskCacheStrategy,
                        transformations,
                        isTransformationRequired,
                        isScaleOnlyOrNoTransform,
                        options,
                        isMemoryCacheable,
                        useUnlimitedSourceExecutorPool,
                        useAnimationPool,
                        onlyRetrieveFromCache,
                        cb,
                        callbackExecutor,
                        key,
                        startTime);
            }
        }

        // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
        // deadlock.
        cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
        return null;
    }
@Nullable
    private EngineResource<?> loadFromMemory(
            EngineKey key, boolean isMemoryCacheable, long startTime) {
    
    
        if (!isMemoryCacheable) {
    
    
            return null;
        }

        EngineResource<?> active = loadFromActiveResources(key);
        if (active != null) {
    
    
            if (VERBOSE_IS_LOGGABLE) {
    
    
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return active;
        }

        EngineResource<?> cached = loadFromCache(key);
        if (cached != null) {
    
    
            if (VERBOSE_IS_LOGGABLE) {
    
    
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return cached;
        }

        return null;
    }

3 缓存总结

1.Glide的缓存是从Engine开始的,首先生成一个EngineKey,从内存中获取,内存缓存分两类,一种是hashmap保存的弱引用的图片,这种图片是当前页面正在显示的图片,一种是lruCache缓存,保存着从hashmap中移除的照片。
2.如果需要的图片内存缓存中都没有,那么会新建一个EngineJob,然后执行EngineRunning从disk缓存中获取,第一次也是根据EngineKey去disk缓存中查找符合要求的图片,如果有返回,执行3步骤。如果没有,则再去寻找是否有这张图原图(即没有处理过宽高的图片),如果有,执行3步骤。如果没有,再去发起网络请求,将得到的图片的原图保存下来,在执行3步骤。
3.处理中这张图片的宽高,在执行3步骤
4.存入hashmap中,标记为再用图片
5.hashmap中的图片没有被引用的对象,则进入lruCache中

4.Glide into完整过程

那么我们就可以描述完整的一次Glide流程
首先,先会处理ImageView的ScaleType,然后把ImageView处理成一个Target,接着看这个target是否有前一个图片加载请求,如果有就标记无效掉他并清空状态,如果没有就创建出一个request并给这个request创建回调及监听(with中创建的空白的fragment),然后通过requestTracker执行这个request。
紧接着,会测量出target的长宽并回调onSizeReady方法,在这个同时会设置占位图。onSizeReady方法里面,除了赋值长宽之外,还会启动Engine的load方法,在load中首先会获取到一个EngineKey,这个key是由长,宽,地址等构成。组件完成之后,先从两级内存缓存中查找是否有所需要的图片,如果有直接返回。然后会查看是否有这个EngineKey的EngineJob在执行,如果有的话,就不再请求了,采用addCallBack回调集合的方式完成图片的下载。如果没有的话,就通过EngineJobFactory创建一个新的EngineJob,同时创建decodeJob和EngineRunnable,然后通过EngineJob来执行EngineRunning。
首先执行的从disk缓存中获取图片,先从disk缓存中,先获取带有长宽限制的图片,再获取原图,如果获取不到,执行从Source中获取图片的方法。这个方法就是请求网络的方法,是由之前在load方法中创建的SteamFetcher来作为下载图片的工具,这个SteamFetcher是由load的对象决定,返回的对象是InputSteam。
下载完成获取到InputSteam之后,decodeJob把InputSteam转化成Resource对象并存下原图与有长宽限制的图片。接着调用EngineRunning的onLoadComplete方法,onLoadComplete会回调EngineJob的onResourceReady方法,onResourceReady会向EngineJob中的handler发送一条消息,并切换到主线程。切换到主线程之后,EngineJob会回调Engine的方法,设置资源为内存缓存,EngineJob又会回调之前callback集合,通知图片已经准备完成,这个callback实现实在GenericRequest中。接下来,EngineJob中的callback会调用target.onResourceReady方法,这个方法最后也会调用到target.setResource方法,把获取到的图片设置到对应的ImagView中。

猜你喜欢

转载自blog.csdn.net/just_hu/article/details/106484009