Glide源码解析---缓存篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/omyrobin/article/details/85600385

Glide的缓存分为两部分

  1. 内存缓存
  2. 硬盘缓存

今天我们从源码的角度来分析。

Glide.with(context).load(url).into(imageView);

上面一行代码我们就可以实现图片的加载,这一行代码背后进行了成吨的工作,我们今天只来看看它关于缓存的部分。

Engine load()

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();
    //生成缓存key
    EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
            loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
            transcoder, loadProvider.getSourceEncoder());
    //从LruCache中获取缓存
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
        cb.onResourceReady(cached);//回调 进行弱引用HashMap缓存的写入
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
        }
        return null;
    }
//在value为弱引用的hashMap获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
    cb.onResourceReady(active);//回调 进行弱引用HashMap缓存的写入
    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);
}
//上诉两种方案都获取不到 EngineResource 开启线程
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);

}

弱引用HashMap的缓存写入

private void handleResultOnMainThread() {
    if (isCancelled) {
        resource.recycle();
        return;
    } else if (cbs.isEmpty()) {
        throw new IllegalStateException("Received a resource without any callbacks to notify");
    }
    //通过EngineResourceFactory构建出了一个包含图片资源的EngineResource对象
    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();
    //回调Engine中写入缓存
    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();
}


@SuppressWarnings("unchecked")
@Override
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()) {
            //存入弱引用HashMap对象中
            activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
        }
    }
    // TODO: should this check that the engine job is still current?
    jobs.remove(key);
}

引用计数

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) {
        //当引用为0时,回调 进行LruCache缓存写入
        listener.onResourceReleased(key, this);
    }
}

LruCache的缓存写入

@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
    Util.assertMainThread();
    activeResources.remove(cacheKey);
    if (resource.isCacheable()) {
        //写入LruCache缓存中
        cache.put(cacheKey, resource);
    } else {
        resourceRecycler.recycle(resource);
    }
}

EngineRunnable线程对象

class EngineRunnable implements Runnable, Prioritized {

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

    @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);
        }
    }
    
    private Resource<?> decode() throws Exception {
        if (isDecodingFromCache()) {
            return decodeFromCache();//从硬盘缓存中获取
        } else {
            return decodeFromSource();//从网络中下载获取
        }
    }
}

内存缓存

LruCache中获取
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);


private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
    //如果skipMemoryCache()传入true则isMemoryCacheable为false 表示跳过内存缓存
    if (!isMemoryCacheable) { 
        return null;
    }
    
    EngineResource<?> cached = getEngineResourceFromCache(key);
    //如果LruCache中有缓存
    if (cached != null) {
        //引用计数++
        cached.acquire();
        //加入到activeResources这个弱引用的hashMap中 活动缓存 正在使用
        activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
    }
    return cached;
}


弱引用中获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);


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 {
            //从弱引用hashMap中移除,添加到LruCache中
            activeResources.remove(key);
        }
    }

    return active;
}

EngineResource缓存对象

引用计数的方式判断当前图片是否正在使用
正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。
缓存先写入activeResources弱引用的HashMap然后引用计数的方式判断是否加入到LruCache类LruResourceCache

在loadFromCache()方法的一开始,首先就判断了isMemoryCacheable是不是false,如果是false的话就直接返回null。这是什么意思呢?其实很简单,我们刚刚不是学了一个skipMemoryCache()方法吗?如果在这个方法中传入true,那么这里的isMemoryCacheable就会是false,表示内存缓存已被禁用。

activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片,我们可以看到,loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。使用activeResources来缓存正在使用中的图片,可以保护这些图片不会被LruCache算法回收掉。

硬盘缓存

DiskCacheStrategy.NONE: 表示不缓存任何内容。
DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。

private Resource<?> decode() throws Exception {
    if (isDecodingFromCache()) {
        return decodeFromCache();//从硬盘缓存中获取
    } else {
        return decodeFromSource();//从网络中下载获取
    }
}

从硬盘缓存中获取decodeFromCache()

private Resource<?> decodeFromCache() throws Exception {
    Resource<?> result = null;
    try {
        //对应DiskCacheStrategy.RESULT表示获取缓存转换后的图片
        result = decodeJob.decodeResultFromCache();
    } catch (Exception e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Exception decoding result from cache: " + e);
        }
    }

    if (result == null) {
        //对应DiskCacheStrategy.SOURCE表示获取缓存原始图片
        result = decodeJob.decodeSourceFromCache();
    }
    return result;
}

会先去调用DecodeJob的decodeResultFromCache()方法来获取缓存,如果获取不到,会再调用decodeSourceFromCache()方法获取缓存,这两个方法的区别其实就是DiskCacheStrategy.RESULT和DiskCacheStrategy.SOURCE这两个参数的区别

//DiskCacheStrategy.RESULT
public Resource<Z> decodeResultFromCache() throws Exception {
    if (!diskCacheStrategy.cacheResult()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = loadFromCache(resultKey);
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    return result;
}

//DiskCacheStrategy.SOURCE
public Resource<Z> decodeSourceFromCache() throws Exception {
    if (!diskCacheStrategy.cacheSource()) {
        return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
    return transformEncodeAndTranscode(decoded);//对图片进行转换和转码
}



private Resource<T> loadFromCache(Key key) throws IOException {
    //从DisLruCache中获取缓存文件
    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;
}

调用getDiskCache()方法获取到的就是Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法并把缓存Key传入,就能得到硬盘缓存的文件了。如果文件为空就返回null,如果文件不为空则将它解码成Resource对象后返回即可

从网络下载获取decodeFromSource()

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 (isCancelled) {
            return null;
        }
        decoded = decodeFromSourceData(data);
    } finally {
        fetcher.cleanup();
    }
    return decoded;
}

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);
    }
    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);
    startTime = LogTime.getLogTime();
    Resource<T> result = loadFromCache(resultKey.getOriginalKey());
    return result;
}

转换后的图片缓存写入

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
    long startTime = LogTime.getLogTime();
    Resource<T> transformed = transform(decoded);
    //转换后的图片缓存写入
    writeTransformedToCache(transformed);
    startTime = LogTime.getLogTime();
    Resource<Z> result = transcode(transformed);
    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);
}

猜你喜欢

转载自blog.csdn.net/omyrobin/article/details/85600385