上一篇文章从源码角度深入理解 Glide(上)中, 我们已经把 Glide 加载图片的基本流程走了一遍, 想必你已经对 Glide 的加载原理有了新的认识并且见识到了 Glide 源码的复杂逻辑, 在我们感叹 Glide 源码复杂的同时我们也忽略了 Glide 加载图片过程的其它细节, 特别是缓存方面, 我们在上一篇文章中对于缓存的处理都是跳过的, 这一篇文章我们就从 Glide 的缓存开始再次对 Glide 进行深入理解.
Glide 缓存
Glide 加载默认情况下可以分为三级缓存, 哪三级呢? 他们分别是内存, 磁盘和网络.
默认情况下, Glide 会在开始一个新的图片请求之前检查以下多级的缓存:
1. 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片
2. 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中
3. 资源类型(Resource) - 该图片是否之前曾被解码, 转换并写入过磁盘缓存
4. 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存
网络级别的加载我们已经在上一篇文章了解了, 上面列出的前两种情况则是内存缓存, 后两种情况则是磁盘缓存, 如果以上四种情况都不存在, Glide 则会通过返回到原始资源以取回数据 (原始文件, Uri, Url(网络) 等)
缓存的 key
提起缓存, 我们首先要明白, Glide 中缓存的图片肯定不止一个, 当我们加载图片的同时, 如果缓存中有我们正在加载的图片, 我们怎么找到这个图片的缓存呢? 所以为了找到对应的缓存, 则每一个缓存都有它对应的标识, 这个标识在 Glide 中用接口 Key 来描述
- /**Key 接口 */
- public interface Key {
- String STRING_CHARSET_NAME = "UTF-8";
- Charset CHARSET = Charset.forName(STRING_CHARSET_NAME);
- void updateDiskCacheKey(@NonNull MessageDigest messageDigest);
- @Override
- boolean equals(Object o);
- @Override
- int hashCode();
- }
缓存 Key 的生成
前面提到了缓存 Key 的接口, 那这个缓存的 Key 实在哪里生成的, 实现类又是什么呢? 这我们就要看到加载发动机 Engine 类的 load 方法
- private final EngineKeyFactory keyFactory;
- /**Engine 类的 load 方法 */
- 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) {
- Util.assertMainThread();
- long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
- EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
- resourceClass, transcodeClass, options);
- // 省略部分代码
- ..........
- }
- /**EngineKey 类 */
- class EngineKey implements Key {
- private final Object model;
- private final int width;
- private final int height;
- private final Class<?> resourceClass;
- private final Class<?> transcodeClass;
- private final Key signature;
- private final Map<Class<?>, Transformation<?>> transformations;
- private final Options options;
- private int hashCode;
- EngineKey(
- Object model,
- Key signature,
- int width,
- int height,
- Map<Class<?>, Transformation<?>> transformations,
- Class<?> resourceClass,
- Class<?> transcodeClass,
- Options options) {
- this.model = Preconditions.checkNotNull(model);
- this.signature = Preconditions.checkNotNull(signature, "Signature must not be null");
- this.width = width;
- this.height = height;
- this.transformations = Preconditions.checkNotNull(transformations);
- this.resourceClass =
- Preconditions.checkNotNull(resourceClass, "Resource class must not be null");
- this.transcodeClass =
- Preconditions.checkNotNull(transcodeClass, "Transcode class must not be null");
- this.options = Preconditions.checkNotNull(options);
- }
- @Override
- public boolean equals(Object o) {
- if (o instanceof EngineKey) {
- EngineKey other = (EngineKey) o;
- return model.equals(other.model)
- && signature.equals(other.signature)
- && height == other.height
- && width == other.width
- && transformations.equals(other.transformations)
- && resourceClass.equals(other.resourceClass)
- && transcodeClass.equals(other.transcodeClass)
- && options.equals(other.options);
- }
- return false;
- }
- @Override
- public int hashCode() {
- if (hashCode == 0) {
- hashCode = model.hashCode();
- hashCode = 31 * hashCode + signature.hashCode();
- hashCode = 31 * hashCode + width;
- hashCode = 31 * hashCode + height;
- hashCode = 31 * hashCode + transformations.hashCode();
- hashCode = 31 * hashCode + resourceClass.hashCode();
- hashCode = 31 * hashCode + transcodeClass.hashCode();
- hashCode = 31 * hashCode + options.hashCode();
- }
- return hashCode;
- }
- }
由以上源码我们知道, 通过 EngineKeyFactory 的 buildKey 方法 Glide 创建了缓存的 Key 实现类 EngineKey 对象, 由生成 EngineKey 对象传入的参数我们可以明白, 只要有一个参数不同, 所生成的 EngineKey 对象都会是不同的. 内存的速度是最快的, 理所当然如果内存中有缓存的对应加载图片 Glide 会搜先从内存缓存中加载.
LRU
Glide 官方文档 https://bumptech.github.io/glide/doc/caching.html 说明
LRU 算法思想
从官方文档描述, 我们可以知道 Glide 的缓存底层实现原理算法都是 LRU(Least Recently Used), 字面意思为最近最少使用. 算法核心思想 (个人理解): 在一个有限的集合中, 存入缓存, 每一个缓存都有唯一标识, 当要获取一个缓存, 集合中没有则存入, 有则直接从集合获取, 存入缓存到集合时如果集合已经满了则找到集合中最近最少的缓存删除并存入需要存入的缓存. 这样也就有效的避免了内存溢出(OOM) 的问题.
接下来我们看一张图能够更好的理解 LRU 算法
横线上方每个数字代表要存入的数据, 横线下方代表三个内存页(也可以理解为缓存结合), 缓存集合最多可以存入三个缓存数据, 则从 1 开始依次按照数字代码的缓存读取并存入缓存集合, 首先开始时三个页内存是空的, 前三个缓存数据不同, 依次存入缓存集合, 当数字 4 在内存中并进行缓存时, 根据 LRU 算法思想, 则 2 和 3 相较于 1 使用时间间隔更少, 所以淘汰 1, 缓存数据 4 替换 1 的位置, 接下去同理.
Glide 内存缓存使用的是 LruCache, 磁盘缓存使用的 DiskLruCache, 他们核心思想都是 LRU 算法, 而缓存集合使用的是 LinkedHashMap, 熟悉集合框架 https://www.jianshu.com/p/972dfad0c95b 应该都明白 LinkedHashMap 集成 HashMap, 并且 LinkedHashMap 保证了 key 的唯一性, 更符合 LRU 算法的实现.
更深入的了解 LruCache 和 DiskLruCache 可以查看郭霖大神的解析 Android DiskLruCache 完全解析, 硬盘缓存的最佳方案
内存缓存
内存缓存相关 API
- // 跳过内存缓存
- RequestOptions requestOptions =new RequestOptions().skipMemoryCache(true);
- Glide.with(this).load(IMAGE_URL).apply(requestOptions).into(imageView);
- //Generated API 方式
- GlideApp.with(this).load(IMAGE_URL).skipMemoryCache(true).into(imageView);
- // 清除内存缓存, 必须在主线程中调用
- Glide.get(context).clearMemory();
内存缓存源码分析
内存缓存不需要你进行任何设置, 它默认就是开启的, 我们再次回到 Engine 类的 load 方法
- /**Engine 类的 load 方法 */
- 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) {
- // 省略部分代码
- ..........
- EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
- if (active != null) {
- cb.onResourceReady(active, DataSource.MEMORY_CACHE);
- if (VERBOSE_IS_LOGGABLE) {
- logWithTimeAndKey("Loaded resource from active resources", startTime, key);
- }
- return null;
- }
- // 省略部分代码
- ..........
- }
活动资源 (Active Resources)
通过以上 Engine 类 load 的源码, 首先调用 loadFromActiveResources 方法来从内存中获取缓存
- private final ActiveResources activeResources;
- /**Engine 类的 loadFromActiveResources 方法 */
- @Nullable
- private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
- if (!isMemoryCacheable) {
- return null;
- }
- EngineResource<?> active = activeResources.get(key);
- if (active != null) {
- active.acquire();
- }
- return active;
- }
- /**ActiveResources 类 */
- final class ActiveResources {
- @VisibleForTesting
- final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
- // 省略部分代码
- ........
- @VisibleForTesting
- static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
- // 省略部分代码
- ........
- }
- }
- /**Engine 类的 onEngineJobComplete 方法 */
- @SuppressWarnings("unchecked")
- @Override
- public void onEngineJobComplete(EngineJob<?> engineJob, 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.activate(key, resource);
- }
- }
- jobs.removeIfCurrent(key, engineJob);
- }
- /**RequestOptions 类的 skipMemoryCache 方法 */
- public RequestOptions skipMemoryCache(boolean skip) {
- if (isAutoCloneEnabled) {
- return clone().skipMemoryCache(true);
- }
- this.isCacheable = !skip;
- fields |= IS_CACHEABLE;
- return selfOrThrowIfLocked();
- }
通过以上源码, 这里需要分几步来解读, 首先如果是第一次加载, 肯定没有内存缓存, 所以如果第一次加载成功, 则在加载成功之后调用了 Engine 对象的 onEngineJobComplete 方法, 并在该方法中将加载成功的 resource 通过 ActiveResources 对象的 activate 方法保存在其内部维护的弱引用(WeakReference)HashMap 中. 下次再加载相同的资源, 当你设置了 skipMemoryCache(true), 则表明你不想使用内存缓存, 这时候 Glide 再次加载相同资源的时候则会跳过内存缓存的加载, 否则可以从 ActiveResources 对象中获取, 如果内存资源没被回收的话(关于弱引用的一下描述可以看看我以前写的一篇文章 Android 学习笔记之图片三级缓存 https://www.jianshu.com/p/bfc3e8cd64ae ). 如果该弱引用资源被回收了(GC), 则下一步就到内存中寻找是否有该资源的缓存.
内存缓存 (Memory cache)
接着回到 Engine 类的 load 方法, 如果弱引用缓存资源已经被回收, 则调用 loadFromCache 方法在内存缓存中查找缓存资源
- /**Engine 类的 load 方法 */
- 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) {
- // 省略部分代码
- ..........
- EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
- if (cached != null) {
- cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
- if (VERBOSE_IS_LOGGABLE) {
- logWithTimeAndKey("Loaded resource from cache", startTime, key);
- }
- return null;
- }
- // 省略部分代码
- ..........
- }
- /**Engine 类的 loadFromCache 方法 */
- private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
- if (!isMemoryCacheable) {
- return null;
- }
- EngineResource<?> cached = getEngineResourceFromCache(key);
- if (cached != null) {
- cached.acquire();
- activeResources.activate(key, cached);
- }
- return cached;
- }
- /**Engine 类的 getEngineResourceFromCache 方法 */
- private final MemoryCache cache;
- 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 /*isMemoryCacheable*/, true /*isRecyclable*/);
- }
- return result;
- }
- /**GlideBuilder 类的 build 方法 */
- private MemoryCache cache;
- @NonNull
- Glide build(@NonNull Context context) {
- // 省略部分代码
- ..........
- if (memoryCache == null) {
- memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
- }
- if (diskCacheFactory == null) {
- diskCacheFactory = new InternalCacheDiskCacheFactory(context);
- }
- if (engine == null) {
- engine =
- new Engine(
- memoryCache,
- diskCacheFactory,
- diskCacheExecutor,
- sourceExecutor,
- GlideExecutor.newUnlimitedSourceExecutor(),
- GlideExecutor.newAnimationExecutor(),
- isActiveResourceRetentionAllowed);
- }
- // 省略部分代码
- ..........
- }
- /**LruResourceCache 类的实现继承关系 */
- public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache{......}
通过以上源码, 在 loadFromCache 同样也判断了 Glide 是否设置了 skipMemoryCache(true)方法, 没有设置则调用 getEngineResourceFromCache 方法, 在该方法中我们可以看到 cache 对象就是 MemoryCache 对象, 而该对象实际是一个接口, 他的实现类是 LruResourceCache, 该对象我们前面在 GlideBuilder 的 build 方法中进行了新建(在第一步 with 方法中调用了 Glide.get 方法, 在 get 方法中初始化 Glide 调用了在 GlideBuilder 的 build 方法), 这里也就说明 Glide 的内存缓存还是使用 LruCache 来实现, 这里如果获取到了内存缓存, 则获取内容缓存的同时移除该缓存, 并在 loadFromCache 方法中将该资源标记为正在使用同时加入在弱引用中. 这样在 ListView 或者 Recyclerview 中加载图片则下次加载首先从弱引用 Map 中获取缓存资源, 并且标志当前资源正在使用, 可以防止该资源被 LRU 算法回收掉.
内存缓存写入
前面我们只是分析了如何获取内存缓存, 而内存缓存又是在哪里写入的呢? 根据前面分析, 首先获取在弱引用 Map 中的缓存资源, 而前面我们在分析活动资源 (Active Resources) 时候已经说过是在 onEngineJobComplete 放中往弱引用 Map 存放缓存资源, 而 onEngineJobComplete 方法是在哪里调用呢, 这我们就要回想起上一篇文章中我们再网络加载图片成功后腰切换在主线程回调来显示图片, 也就是 EngineJob 对象的 handleResultOnMainThread 方法
- /**EngineJob 类的 handleResultOnMainThread 方法 */
- @Synthetic
- void handleResultOnMainThread() {
- // 省略部分代码
- ..........
- engineResource = engineResourceFactory.build(resource, isCacheable);
- hasResource = true;
- // 省略部分代码
- ..........
- engineResource.acquire();
- listener.onEngineJobComplete(this, key, engineResource);
- engineResource.release();
- // 省略部分代码
- ..........
- }
- /**EngineJob 类的 EngineResourceFactory 内部类 */
- @VisibleForTesting
- static class EngineResourceFactory {
- public <R> EngineResource<R> build(Resource<R> resource, boolean isMemoryCacheable) {
- return new EngineResource<>(resource, isMemoryCacheable, /*isRecyclable=*/ true);
- }
- }
- /**Engine 类的 onEngineJobComplete 方法 */
- @SuppressWarnings("unchecked")
- @Override
- public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
- // 省略部分代码
- ..........
- if (resource != null) {
- resource.setResourceListener(key, this);
- if (resource.isCacheable()) {
- activeResources.activate(key, resource);
- }
- }
- // 省略部分代码
- ..........
- }
通过以上源码, EngineJob 类的 handleResultOnMainThread 方法首先构建了获取好的包含图片的资源, 标记当前资源正在使用, 通过 listener.onEngineJobComplete 回调, 而 listener 就是 Engine 对象, 也就到了 Engine 类的 onEngineJobComplete 方法, 并在该方法中存入了图片资源到弱引用 Map 中.
上面我是分析了弱引用资源的缓存存入, 接着我们看看内存缓存是在哪里存入的, 在次看回 handleResultOnMainThread 方法, 我们看到 onEngineJobComplete 回调前后分别调用了 EngineResource 对象的 acquire 方法和 release 方法
- /**EngineResource 类的 acquire 方法 */
- 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;
- }
- /**EngineResource 类的 release 方法 */
- 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 类的 onResourceReleased 方法 */
- @Override
- public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
- Util.assertMainThread();
- activeResources.deactivate(cacheKey);
- if (resource.isCacheable()) {
- cache.put(cacheKey, resource);
- } else {
- resourceRecycler.recycle(resource);
- }
- }
通过以上源码, 其实我们应该能恍然大悟, Glide 的内存缓存存入其实就是通过一个 acquired 变量来进行控制, 如果当前弱引用资源不再使用, 也就是 acquired 等于零的时候, 则调用回调 listener.onResourceReleased(listener 就是 Engine 对象), 在 onResourceReleased 方法中移除了弱引用资源资源, 并且没有设置 skipMemoryCache(true), 则通过 cache.put 存入内存缓存.
总的来说 Glide 的内存缓存主要是结合了弱引用和内存来实现的.
Glide 内存缓存机制示意图
磁盘缓存
说去磁盘缓存, 上一篇文章我们在简单使用 Glide 的例子中就已经使用了 Glide 的磁盘缓存
- RequestOptions requestOptions = new RequestOptions()
- .diskCacheStrategy(DiskCacheStrategy.NONE);// 不使用缓存
- Glide.with(Context).load(IMAGE_URL).apply(requestOptions).into(mImageView);
既然知道如何使用 Glide 的磁盘缓存, 首先我们要了解 Glide4 中给我提供了哪几种磁盘缓存策略
磁盘缓存策略
1.DiskCacheStrategy.NONE: 表示不使用磁盘缓存
2.DiskCacheStrategy.DATA: 表示磁盘缓存只缓存原始加载的图片
DiskCacheStrategy.RESOURCE: 表示磁盘缓存只缓存经过解码转换后的图片
DiskCacheStrategy.ALL: 表示磁盘缓存既缓存原始图片, 也缓存经过解码转换后的图片
DiskCacheStrategy.AUTOMATIC: 表示让 Glide 根据图片资源智能地选择使用哪一种磁盘缓存策略, 该选项也是我们在不进行手动设置的时候 Glide 的默认设置
磁盘缓存源码分析
不知你是否还记得上一篇文章中在加载图片的时候我们是在开启子线程任务在线程池中进行的, 我们来回顾一下
- /**DecodeJob 类的 start 方法 */
- public void start(DecodeJob<R> decodeJob) {
- this.decodeJob = decodeJob;
- GlideExecutor executor = decodeJob.willDecodeFromCache()
- ? diskCacheExecutor
- : getActiveSourceExecutor();
- executor.execute(decodeJob);
- }
- /**DecodeJob 类的 willDecodeFromCache 方法 */
- boolean willDecodeFromCache() {
- Stage firstStage = getNextStage(Stage.INITIALIZE);
- return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
- }
- /**DecodeJob 类的 getNextStage 方法 */
- 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;
- // 省略部分代码
- ......
- }
- }
- /**DiskCacheStrategy 类的 ALL 对象 */
- public static final DiskCacheStrategy ALL = new DiskCacheStrategy() {
- @Override
- public boolean isDataCacheable(DataSource dataSource) {
- return dataSource == DataSource.REMOTE;
- }
- @Override
- public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
- EncodeStrategy encodeStrategy) {
- return dataSource != DataSource.RESOURCE_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
- }
- @Override
- public boolean decodeCachedResource() {
- return true;
- }
- @Override
- public boolean decodeCachedData() {
- return true;
- }
- };
- /**GlideBuilder 类的 build 方法 */
- @NonNull
- Glide build(@NonNull Context context) {
- // 省略部分代码
- ......
- if (diskCacheExecutor == null) {
- diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
- }
- // 省略部分代码
- ......
- }
- /**GlideExecutor 类的 newDiskCacheExecutor 方法 */
- private static final int DEFAULT_DISK_CACHE_EXECUTOR_THREADS = 1;
- public static GlideExecutor newDiskCacheExecutor() {
- return newDiskCacheExecutor(
- DEFAULT_DISK_CACHE_EXECUTOR_THREADS,
- DEFAULT_DISK_CACHE_EXECUTOR_NAME,
- UncaughtThrowableStrategy.DEFAULT);
- }
- /**GlideExecutor 类的 newDiskCacheExecutor 方法 */
- public static GlideExecutor newDiskCacheExecutor(
- int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
- return new GlideExecutor(
- new ThreadPoolExecutor(
- threadCount /* corePoolSize */,
- threadCount /* maximumPoolSize */,
- 0 /* keepAliveTime */,
- TimeUnit.MILLISECONDS,
- new PriorityBlockingQueue<Runnable>(),
- new DefaultThreadFactory(name, uncaughtThrowableStrategy, true)));
- }
通过以上源码, 可以分两个步骤来进行解读:
第一步在 DecodeJob 对象的 start 方法开启子线程来加载图片, 这里使用了线程池, 通过 willDecodeFromCache 方法和 getNextStage 放结合, 主要通过 Stage 枚举来判断当前使用的缓存策略, 而缓存策略的设置则通过 DiskCacheStrategy 对象的 decodeCachedResource 和 decodeCachedData 方法来进行设置, 而这两个方法在 DiskCacheStrategy 抽象类都是抽象方法, 而他们的实现就是我们前面提到的 Glide 磁盘缓存的五种策略, 上面代码中列出其中一种 ALL 代码, decodeCachedResource 和 decodeCachedData 方法都返回 ture, 也就说明磁盘缓存既缓存原始图片, 也缓存经过解码转换后的图片; 如果 decodeCachedResource 返回 false 和 decodeCachedData 方法返回 true, 也就代表 DATA 策略, 磁盘缓存只缓存原始加载的图片, 其他同理
第二步通过前面对设置策略的判断, 如果有缓存策略, 则拿到的线程池就是磁盘缓存加载的线程池(线程池的理解可以看看我以前写的一篇文章), 该线程的初始化还是在 GlideExecutor 对象的 build 方法中, 通过以上源码, 该线程池只有唯一一个核心线程, 这就保证所有执行的的任务都在这一个线程中执行, 并且是顺序执行, 也就不用在考虑线程同步的问题了.
根据前面官网的说明, 不管是内存缓存还是磁盘缓存, 都是使用 LRU, 接着看看 Glide 磁盘缓存在哪获取 LRU 对象, 还是得看到 GlideBuilder 对象的 build 方法
- /**GlideBuilder 类的 build 方法 */
- private DiskCache.Factory diskCacheFactory;
- @NonNull
- Glide build(@NonNull Context context) {
- // 省略部分代码
- ..........
- if (diskCacheFactory == null) {
- diskCacheFactory = new InternalCacheDiskCacheFactory(context);
- }
- // 省略部分代码
- ..........
- }
- /**InternalCacheDiskCacheFactory 类的继承关系 */
- public final class InternalCacheDiskCacheFactory extends DiskLruCacheFactory {
- // 省略实现代码
- ..........
- }
- /**DiskLruCacheFactory 类的部分代码 */
- public class DiskLruCacheFactory implements DiskCache.Factory {
- // 省略部分代码
- ..........
- @Override
- public DiskCache build() {
- File cacheDir = cacheDirectoryGetter.getCacheDirectory();
- // 省略部分代码
- ..........
- return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
- }
- }
- /**DiskLruCacheWrapper 类的部分代码 */
- public class DiskLruCacheWrapper implements DiskCache {
- // 省略部分代码
- ..........
- private synchronized DiskLruCache getDiskCache() throws IOException {
- if (diskLruCache == null) {
- diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
- }
- return diskLruCache;
- }
- // 省略部分代码
- ..........
- }
- /**DiskLruCache 类的部分代码 */
- public final class DiskLruCache implements Closeable {
- // 省略部分代码
- ..........
- public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
- throws IOException {
- DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
- // 省略部分代码
- ..........
- return cache;
- }
- // 省略部分代码
- ..........
- }
通过以上源码, 我们在 GlideBuilder 对象的 build 方法中已经新建了 InternalCacheDiskCacheFactory 对象, 也就是 DiskLruCacheFactory 对象, 同样该对象已经被我们传入 Engine 对象的构造方法中, 最终包装成 LazyDiskCacheProvider 对象(该对象代码就不贴出了), 所以只要调用 DiskLruCacheFactory 对象的 build 方法就能够最终获取到 DiskLruCache 对象, 该对象是 Glide 自己实现的, 但是其原理和谷歌官方推荐的 DiskLruCache 也差不了太多, 核心还是使用 LRU 算法来实现磁盘缓存.
资源类型(Resource)
根据前面分分析, 假定没有内存缓存, 而是由磁盘缓存, 则结合前面分析我们得到了磁盘缓存处理的线程池, 也获得枚举 Stage 是 RESOURCE_CACHE 或 DATA_CACHE, 则在 DecodeJob 对象 getNextGenerator 方法, 我们就能得到对应的 Generator
- /**DecodeJob 的 getNextGenerator 方法 */
- 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);
- }
- }
通过 getNextGenerator 方法的源码, 如果之前设置磁盘缓存策略为 DiskCacheStrategy.RESOURCE, 则应该对应的就是枚举 Stage.RESOURCE_CACHE, 也就是说接下来使用的资源 Generator 是 ResourceCacheGenerator, 结合上一篇文章, 我们分析网络加载流程是这里获取的是 SourceGenerator, 我们接着来看 ResourceCacheGenerator 的 startNext()方法
- /** ResourceCacheGenerator 类的 startNext 方法 */
- @SuppressWarnings("PMD.CollapsibleIfStatements")
- @Override
- public boolean startNext() {
- // 省略部分代码
- ..........
- currentKey =
- new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
- helper.getArrayPool(),
- sourceId,
- helper.getSignature(),
- helper.getWidth(),
- helper.getHeight(),
- transformation,
- resourceClass,
- helper.getOptions());
- cacheFile = helper.getDiskCache().get(currentKey);
- if (cacheFile != null) {
- 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;
- }
- /**DecodeHelper 类的 getDiskCache 方法 */
- DiskCache getDiskCache() {
- return diskCacheProvider.getDiskCache();
- }
- /** LazyDiskCacheProvider 类的 getDiskCache 方法 */
- @Override
- public DiskCache getDiskCache() {
- if (diskCache == null) {
- synchronized (this) {
- if (diskCache == null) {
- diskCache = factory.build();
- }
- if (diskCache == null) {
- diskCache = new DiskCacheAdapter();
- }
- }
- }
- return diskCache;
- }
通过以上源码其实已经很清晰, 首先还是获取缓存的唯一 key, 然后 helper.getDiskCache().get(currentKey)这一句话就是获取缓存, helper 对象就是 DecodeHelper, 它的 getDiskCache 方法获取的对象也就是前面提到的包含 DiskLruCacheFactory 对象的 LazyDiskCacheProvider 对象, 而 LazyDiskCacheProvider 对象的 getDiskCache 方法调用了 factory.build(),factory 对象 DiskLruCacheFactory, 也就是获取了我们前面所说的 DiskLruCache 对象.
接着继续看数据返回走的流程还是通过回调通 cb.onDataFetcherReady 将获取的缓存资源传递到 DecodeJob, 由 DecodeJob 继续执行剩余图片显示步骤, 大致流程和网络加载差不多, 这里就不进行讨论了
- /** ResourceCacheGenerator 类的 startNext 方法 */
- @Override
- public void onDataReady(Object data) {
- cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE,
- currentKey);
- }
数据来源 (Data)
同理资源类型 (Resource), 则设置磁盘缓存策略为 DiskCacheStrategy.DATA, 则应该对应的就是枚举 Stage.DATA_CACHE, 使用的资源 Generator 是 DataCacheGenerator, 所以直接看看 DataCacheGenerator 的 startNext() 方法, 该方法源码如下, 同样是根据 key 通过 DiskLruCache 对象来获取磁盘缓存(DATA), 数据返回走的流程还是通过回调通 cb.onDataFetcherReady 将获取的缓存资源传递到 DecodeJob, 由 DecodeJob 继续执行剩余图片显示
- /** DataCacheGenerator 类的 startNext 方法 */
- @Override
- public boolean startNext() {
- // 省略部分代码
- ..........
- 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;
- }
- /** DataCacheGenerator 类的 onDataReady 方法 */
- @Override
- public void onDataReady(Object data) {
- cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
- }
磁盘缓存数据存入
前面我们只是了解了磁盘缓存的获取, 磁盘缓存又是在哪里存入的, 接着往下看.
根据上一篇文章的分析, 加载图片会走到 DecodeJob 对象的 decodeFromRetrievedData 方法
- /** DecodeJob 类的 decodeFromRetrievedData 方法 */
- private void decodeFromRetrievedData() {
- // 省略部分代码
- ..........
- notifyEncodeAndRelease(resource, currentDataSource);
- // 省略部分代码
- ..........
- }
- /** DecodeJob 类的 notifyEncodeAndRelease 方法 */
- private final DeferredEncodeManager<?> deferredEncodeManager = new DeferredEncodeManager<>();
- private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
- // 省略部分代码
- ..........
- stage = Stage.ENCODE;
- try {
- if (deferredEncodeManager.hasResourceToEncode()) {
- deferredEncodeManager.encode(diskCacheProvider, options);
- }
- }
- // 省略部分代码
- ..........
- }
- /** DeferredEncodeManager 类的 encode 方法 **/
- void encode(DiskCacheProvider diskCacheProvider, Options options) {
- GlideTrace.beginSection("DecodeJob.encode");
- try {
- diskCacheProvider.getDiskCache().put(key,
- new DataCacheWriter<>(encoder, toEncode, options));
- } finally {
- toEncode.unlock();
- GlideTrace.endSection();
- }
- }
通过以上源码可以看到 DecodeJob 对象的 decodeFromRetrievedData 方法通过调用 notifyEncodeAndRelease 方法, 在该方法中调用了内部类 DeferredEncodeManager 的 encode 方法存入了磁盘缓存, 这里存入的是转换后的磁盘缓存(Resource).
原始数据也就是 SourceGenerator 第一次网络下载成功之后获取的图片数据, 之后再做磁盘缓存, 所以再次回到看到 SourceGenerator 的 onDataReady 方法
- /**SourceGenerator 类的 onDataReady 方法 **/
- private Object dataToCache;
- @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);
- }
- }
- /**SourceGenerator 类的 startNext 方法 **/
- @Override
- public boolean startNext() {
- if (dataToCache != null) {
- Object data = dataToCache;
- dataToCache = null;
- cacheData(data);
- }
- if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
- return true;
- }
- // 省略部分代码
- ..........
- }
- /**SourceGenerator 类的 cacheData 方法 **/
- 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);
- }
- // 省略部分代码
- ..........
- sourceCacheGenerator =
- new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
- }
- /**DecodeJob 类的 reschedule 方法 **/
- @Override
- public void reschedule() {
- runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
- callback.reschedule(this);
- }
- /**Engine 类的 reschedule 方法 **/
- @Override
- public void reschedule(DecodeJob<?> job) {
- getActiveSourceExecutor().execute(job);
- }
通过以上源码, 其实逻辑已经很清晰, 会让你有 "柳暗花明又一村" 的感觉, onDataReady 网络请求成功并且设置了缓存策略, 则将图片资源赋值给 Object 类型的 dataToCache, 执行回调 cb.reschedule,cb 就是 DecodeJob 对象, 所以接着执行了 DecodeJob 对象的 reschedule 方法, 该方法再次执行回调也就是执行了 Engine 对象的 reschedule 方法, 该方法再次执行 DecodeJob, 也就会再次触发 SourceGenerator 类的 startNext 方法, 该方法首先判断了 Object 类型的 dataToCache 是否有值, 前面分析该对象已经赋值, 所以就进入到 SourceGenerator 对象的 cacheData 方法存入了我们的原始下载图片的缓存.
仅从缓存加载图片
前面我基本把 Glide 的缓存模块梳理了一遍, 但是还差个东西, 那就是如果我只想 Glide 加载缓存呢? 这种需求还是有的, 比如说我们在有些应用看到的省流量模式, 不就是正好对应这个需求, 没关系 Gldie 也已经为我们考虑到了, 那就是 onlyRetrieveFromCache(true), 只要设置了这个, 图片在内存缓存或在磁盘缓存中就会被加载出来, 而没有缓存, 则这一次加载失败.
我们看看如何使用
- RequestOptions requestOptions = new RequestOptions().onlyRetrieveFromCache(true);
- Glide.with(this).load(IMAGE_URL).apply(requestOptions).into(mImageView);
- //Generated API 方式
- GlideApp.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .into(mImageView);
使用起来还是很方便的, 只要设置 onlyRetrieveFromCache(true)方法就行, 而它的原理也其实也很简单, 我们再次回到 DecodeJob 对象的 getNextStage 方法, 如果前面获取了缓存, 则相应得到对应的 Generator 加载图片, 如果获取不到缓存, 则枚举 Stage.FINISHED,DecodeJob 对象的 getNextGenerator 方法则会返回 null.(如下代码所示)
- /**DecodeJob 类的 getNextStage 方法 **/
- private Stage getNextStage(Stage current) {
- switch (current) {
- // 省略部分代码
- ..........
- case DATA_CACHE:
- return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
- case SOURCE:
- case FINISHED:
- return Stage.FINISHED;
- }
- }
- /**DecodeJob 类的 getNextGenerator 方法 **/
- private DataFetcherGenerator getNextGenerator() {
- switch (stage) {
- // 省略部分代码
- ..........
- case FINISHED:
- return null;
- default:
- throw new IllegalStateException("Unrecognized stage:" + stage);
- }
- }
Glide 磁盘缓存机制示意图
Glide 缓存小结
通过前面对 Glide 缓存的分析, 让我再次认识到 Glide 的强大, 使用时只是简单的几个方法设置或者不设置, Glide 都能够在背后依靠其复杂的逻辑为我们快速的加载出图片并显示, 缓存还有一些细节比如可以自定义 key 等, 这里就不进行展开了, 有兴趣的可以自行研究.
Glide 回调与监听
图片加载成功回调原理
由上一篇文章分析, 我们来回顾一下图片加载成功之后的逻辑. 数据加载成功之后切换主线程最终调用 SingleRequest 类的 onResourceReady 方法, 在该方法中加载成功的数据通过 target.onResourceReady 方法将数据加载出来, target 就是 DrawableImageViewTarget 对象, 他继续了实现了 Target 接口的基类 ImageViewTarget, 所以调用它实现的 onResourceReady 方法或者父类实现的 onResourceReady 方法就实现了加载成功数据的回调, 并由 DrawableImageViewTarget 对象显示加载成功的图片, 这就是数据加载成功回调原理.
- /**SingleRequest 类的 onResourceReady 方法 **/
- @Nullable
- private List<RequestListener<R>> requestListeners;
- private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
- // 省略部分代码
- ..........
- isCallingCallbacks = true;
- try {
- boolean anyListenerHandledUpdatingTarget = false;
- // 省略部分代码
- ..........
- if (!anyListenerHandledUpdatingTarget) {
- Transition<? super R> animation =
- animationFactory.build(dataSource, isFirstResource);
- target.onResourceReady(result, animation);
- }
- }
- // 省略部分代码
- ..........
- }
- /**Target 接口 **/
- public interface Target<R> extends LifecycleListener {}
- /**ImageViewTarget 类的 onResourceReady 方法 **/
- @Override
- public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
- if (transition == null || !transition.transition(resource, this)) {
- setResourceInternal(resource);
- } else {
- maybeUpdateAnimatable(resource);
- }
- }
- /**ImageViewTarget 类的 setResourceInternal 方法 **/
- private void setResourceInternal(@Nullable Z resource) {
- setResource(resource);
- maybeUpdateAnimatable(resource);
- }
- DrawableImageViewTarget
- /**DrawableImageViewTarget 类的 setResource 方法 **/
- @Override
- protected void setResource(@Nullable Drawable resource) {
- view.setImageDrawable(resource);
- }
Glide 监听(listener)
再次看看 Glide 监听 (listener) 的例子
- Glide.with(this).load(IMAGE_URL).listener(new RequestListener<Drawable>() {
- @Override
- public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
- return false;
- }
- @Override
- public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
- return false;
- }
- }).into(mImageView);
Glide 监听的实现同样还是基于我们上面分析的 SingleRequest 对象的 onResourceReady 方法, 使用的时候调用 RequestBuilder 对象的 listener 方法, 传入的 RequestListener 对象加入到 requestListeners, 这样在 SingleRequest 对象的 onResourceReady 方法中遍历 requestListeners, 来回调 listener.onResourceReady 方法, 布尔类型的 anyListenerHandledUpdatingTarget 则接收回调 listener.onResourceReady 方法的返回值, 如果返回 true, 则不会执会往下执行, 则接着的 into 方法就不会被触发, 说明我们自己在监听中处理, 返回 false 则不拦截.
- /**RequestBuilder 类的 listener 方法 **/
- @Nullable
- private List<RequestListener<TranscodeType>> requestListeners;
- public RequestBuilder<TranscodeType> listener(
- @Nullable RequestListener<TranscodeType> requestListener) {
- this.requestListeners = null;
- return addListener(requestListener);
- }
- /**RequestBuilder 类的 addListener 方法 **/
- public RequestBuilder<TranscodeType> addListener(
- @Nullable RequestListener<TranscodeType> requestListener) {
- if (requestListener != null) {
- if (this.requestListeners == null) {
- this.requestListeners = new ArrayList<>();
- }
- this.requestListeners.add(requestListener);
- }
- return this;
- }
- /**SingleRequest 类的 onResourceReady 方法 **/
- @Nullable
- private List<RequestListener<R>> requestListeners;
- private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
- // 省略部分代码
- ..........
- isCallingCallbacks = true;
- try {
- boolean anyListenerHandledUpdatingTarget = false;
- if (requestListeners != null) {
- for (RequestListener<R> listener : requestListeners) {
- anyListenerHandledUpdatingTarget |=
- listener.onResourceReady(result, model, target, dataSource, isFirstResource);
- }
- }
- anyListenerHandledUpdatingTarget |=
- targetListener != null
- && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
- if (!anyListenerHandledUpdatingTarget) {
- Transition<? super R> animation =
- animationFactory.build(dataSource, isFirstResource);
- target.onResourceReady(result, animation);
- }
- }
- // 省略部分代码
- ..........
- }
Target(目标)
Target 在 Glide 中相当于中间人的作用, 在图片的展示起到承上启下的功效, 首先看看 Target 接口的继承关系图
通过该图, 我们可以把 Target 分为三类, 一种是简单的 Target, 一种是加载到特定 View 的 Target(ViewTarget), 还有一种是 FutureTarget, 可以知道异步执行的结果, 得到缓存文件
上一篇文章分析 into 方法时我们是分析 into(ImageView)这个方法开始的, 它内部还是会得到特定的 Target 对象, 也就是我们一直说的 DrawableImageViewTarget, 而他是属于 ViewTarget 的子类
简单的 Target(SimpleTarget)
SimpleTarget 其实是在给我们更灵活的加载到各种各样对象准备的, 只要指定我们加载获取的是什么对象 asBitmap(), 就能使用 SimpleTarge 或者集成它的我们自定义的对象, 在其中通过获取的 Bitmap 显示在对应的控件上, 比如上一篇文章例子提到的 NotifivationTarget, 就是加载到指定的 Notifivation 中, 灵活加载.
- // 注意需要指定 Glide 的加载类型 asBitmap, 不指定 Target 不知道本身是是类型的 Target
- Glide.with(this).asBitmap().load(IMAGE_URL).into(new SimpleTarget<Bitmap>() {
- @Override
- public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
- // 加载完成已经在主线程
- mImageView.setImageBitmap(resource);
- }
- });
特定 View 的 Target(ViewTarget)
由 DrawableImageViewTarget 和 BitmapImageViewTarget 我们就可以知道这是为了不同类型的图片资源准备的 Target, 但是还有一种需求, 就是如果我们传入是要加载图片资源的 View, 但是该 View 不被 Glide 支持, 目前 into 方法支持传入 ImageView, 没关系, ViewTarget 可以帮上忙, 比如我们需要加载到 RelativeLayout
- /**
- * @author maoqitian
- * @Description: 自定义 RelativeLayout
- * @date 2019/2/18 0018 19:51
- */
- public class MyView extends RelativeLayout {
- private ViewTarget<MyView, Drawable> viewTarget;
- public MyView(Context context) {
- super(context);
- }
- public MyView(Context context, AttributeSet attrs) {
- super(context, attrs);
- viewTarget =new ViewTarget<MyView, Drawable>(this) {
- @Override
- public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
- setBackground(resource);
- }
- };
- }
- public ViewTarget<MyView, Drawable> getViewTarget() {
- return viewTarget;
- }
- }
- // 使用 Glide 加载
- MyView rl_view = findViewById(R.id.rl_view);
- Glide.with(this).load(IMAGE_URL).into(rl_view.getViewTarget());
FutureTarget
FutureTarget 的一大用处就是可以得到缓存文件
- new Thread(new Runnable() {
- @Override
- public void run() {
- FutureTarget<File> target = null;
- RequestManager requestManager = Glide.with(MainActivity.this);
- try {
- target = requestManager
- .downloadOnly()
- .load(IMAGE_URL)
- .submit();
- final File downloadedFile = target.get();
- Log.i(TAG,"缓存文件路径"+downloadedFile.getAbsolutePath());
- } catch (ExecutionException | InterruptedException e) {
- } finally {
- if (target != null) {
- target.cancel(true); // mayInterruptIfRunning
- }
- }
- }
- }).start();
preload(预加载)
如何使用
Glide.with(this).load(IMAGE_URL).preload();
预加载其实也是属于 Target 的范围, 只是他加载的对象为空而已, 也就是没有加载目标
- /**RequestBuilder 类的 preload 方法 **/
- @NonNull
- public Target<TranscodeType> preload() {
- return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
- }
- /**RequestBuilder 类的 preload 方法 **/
- @NonNull
- public Target<TranscodeType> preload(int width, int height) {
- final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
- return into(target);
- }
- /**RequestBuilder 类的 onResourceReady 方法 **/
- public final class PreloadTarget<Z> extends SimpleTarget<Z> {
- private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() {
- @Override
- public boolean handleMessage(Message message) {
- if (message.what == MESSAGE_CLEAR) {
- ((PreloadTarget<?>) message.obj).clear();
- return true;
- }
- return false;
- }
- });
- // 省略部分代码
- ..........
- public static <Z> PreloadTarget<Z> obtain(RequestManager requestManager, int width, int height) {
- return new PreloadTarget<>(requestManager, width, height);
- }
- @Override
- public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
- HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
- }
- // 省略部分代码
- ..........
- }
通过以上源码, 逻辑已经非常清晰, Glide 的 preload 方法里使用的继承 SimpleTarget 的 PreloadTarget 对象来作为 Target, 在它的 onResourceReady 方法中并没有任何的加载操作, 只是调用了 Handler 来释放资源, 到这里也许你会有疑惑, 不是说预加载么, 怎么不加载. 哈哈, 其实到 onResourceReady 方法被调用经过前面的分析 Glide 已经走完缓存的所有逻辑, 那就很容易理解了, 预加载只是把图片加载到缓存当中, 没有进行其他操作, 自然是预加载, 并且加载完成之后释放了资源.
Generated API
Generated API 说白了就是 Glide 使用注解处理器生成一个 API(GlideApp), 该 API 可以代替 Glide 帮助我们完成图片加载.
Generated API 目前仅可以在 Application 模块内使用, 使用 Generated API 一方面在 Application 模块中可将常用的选项组打包成一个选项在 Generated API 中使用, 另一方面可以为 Generated API 扩展自定义选项(扩展我们自定义的功能方法).
在上一篇文章中例子中我们可以看到使用 Generated API 之后使用 Glide 的方式基本上和 Glide3 的用法一样流式 API 使用, 先来回顾一下如何使用 Generated API
- // 在 App 下的 gradle 添加 Glide 注解处理器的依赖
- dependencies {
- annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
- }
- // 新建一个类集成 AppGlideModule 并添加上 @GlideModule 注解, 重新 rebuild 项目就可以使用 GlideApp 了
- @GlideModule
- public final class MyAppGlideModule extends AppGlideModule {}
经过上的代码的操作, 通过 Glide 注解处理器已经给我们生成了 GlideApp 类
- /**GlideApp 类部分代码 **/
- public final class GlideApp {
- // 省略部分代码
- ..........
- @NonNull
- public static GlideRequests with(@NonNull Context context) {
- return (GlideRequests) Glide.with(context);
- }
- // 省略部分代码
- ..........
- }
- /**GlideApp 类部分代码 **/
- public class GlideRequest<TranscodeType> extends RequestBuilder<TranscodeType> implements Cloneable {
- // 省略部分代码
- ..........
- @NonNull
- @CheckResult
- public GlideRequest<TranscodeType> placeholder(@Nullable Drawable drawable) {
- if (getMutableOptions() instanceof GlideOptions) {
- this.requestOptions = ((GlideOptions) getMutableOptions()).placeholder(drawable);
- } else {
- this.requestOptions = new GlideOptions().apply(this.requestOptions).placeholder(drawable);
- }
- return this;
- }
- // 省略部分代码
- ..........
- }
- /**RequestBuilder 类的 getMutableOptions 方法 **/
- protected RequestOptions getMutableOptions() {
- return defaultRequestOptions == this.requestOptions
- ? this.requestOptions.clone() : this.requestOptions;
- }
通过以上源码, 可以发现, GlideApp 对象的 with 方法返回的是 GlideRequests 对象, GlideRequests 对象继承的是 RequestBuilder, 这时应该又是豁然开朗的感觉, GlideApp 能够适应流式 API, 其实就是对 RequestBuilder 包装了一层, GlideRequests 对象通过其父类 RequestBuilder 对象的 getMutableOptions 方法获取到 requestOptions, 然后在相应的方法中操作 requestOptions 以达到可以使用流式 API 的功能.
GlideExtension
GlideExtension 字面意思就是 Glide 扩展, 它是一个作用于类上的注解, 任何扩展 Glide API 的类都必须使用这个注解来标记, 否则其中被注解的方法就会被忽略. 被 @GlideExtension 注解的类应以工具类的思维编写. 这种类应该有一个私有的, 空的构造方法, 应为 final 类型, 并且仅包含静态方法.
@GlideOption
GlideOption 注解是用来扩展 RequestOptions, 扩展功能方法第一个参数必须是 RequestOptions. 下面我们通过设置一个扩展默认设置占位符和错误符方法的例子来说明 GlideOption 注解.
- /**
- * @author maoqitian
- * @Description: GlideApp 功能扩展类
- * @date 2019/2/19 0019 12:51
- */
- @GlideExtension
- public class MyGlideExtension {
- private MyGlideExtension() {
- }
- // 可以为方法任意添加参数, 但要保证第一个参数为 RequestOptions
- /**
- * 设置通用的加载占位图和错误加载图
- * @param options
- */
- @GlideOption
- public static void normalPlaceholder(RequestOptions options) {
- options.placeholder(R.drawable.ic_cloud_download_black_24dp).error(R.drawable.ic_error_black_24dp);
- }
- }
- /**GlideOptions 类中生成对应的方法 **/
- /**
- * @see MyGlideExtension#normalPlaceholder(RequestOptions)
- */
- @CheckResult
- @NonNull
- public GlideOptions normalPlaceholder() {
- if (isAutoCloneEnabled()) {
- return clone().normalPlaceholder();
- }
- MyGlideExtension.normalPlaceholder(this);
- return this;
- }
- /**GlideRequest 类中生成对应的方法 **/
- /**
- * @see GlideOptions#normalPlaceholder()
- */
- @CheckResult
- @NonNull
- public GlideRequest<TranscodeType> normalPlaceholder() {
- if (getMutableOptions() instanceof GlideOptions) {
- this.requestOptions = ((GlideOptions) getMutableOptions()).normalPlaceholder();
- } else {
- this.requestOptions = new GlideOptions().apply(this.requestOptions).normalPlaceholder();
- }
- return this;
- }
如上代码所示, 我们可以通过 @GlideExtension 注解设置自己功能扩展类, 使用 @GlideOption 注解标注对赢扩展功能静态方法, 重构项目后 Glide 注解处理器则会自动在 GlideOptions 对象和 GlideRequest 对象中生成相应的方法能够被我们调用
- // 调用我们刚刚设置的扩展功能方法
- GlideApp.with(this).load(IMAGE_URL)
- .normalPlaceholder()
- .into(mImageView);
GlideType
GlideType 注解是用于扩展 RequestManager 的, 同理扩展的方法第一个参数必须是 RequestManager, 并设置类型为加载资源类型, 该注解主要作用就是扩展 Glide 支持加载资源的类型, 以下举出官方文档支持 gif 的一个例子, 还是在我们刚刚扩展功能类中.
- @GlideExtension
- public class MyGlideExtension {
- private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();
- @GlideType(GifDrawable.class)
- public static void asMyGif(RequestBuilder<GifDrawable> requestBuilder) {
- requestBuilder
- .transition(new DrawableTransitionOptions())
- .apply(DECODE_TYPE_GIF);
- }
- }
- /**GlideRequests 类中生成的 asMyGif 方法 **/
- /**
- * @see MyGlideExtension#asMyGif(RequestBuilder)
- */
- @NonNull
- @CheckResult
- public GlideRequest<GifDrawable> asMyGif() {
- GlideRequest<GifDrawable> requestBuilder = this.as(GifDrawable.class);
- MyGlideExtension.asMyGif(requestBuilder);
- return requestBuilder;
- }
同理在我们加载 Gif 资源的时候可以直接使用
- GlideApp.with(this).asMyGif().load(IMAGE_URL)
- .into(mImageView);
源码阅读方法思路
看了这么多源码, 其实我想说说框架源码阅读的方法思路:
1. 首先自己能把框架大体的流程走一遍, 然后根据自己刚刚的思路把文章写出来, 在写文章的同时也能发现自己刚刚的思路是否有问题, 慢慢纠正
2. 文章写完, 把整体流程图画出来, 画图的过程一个是复习思路, 还可以让自己对源码逻辑更加清晰
3. 阅读框架源码时看到英文注释可以先理解其含义, 在你看源码没头绪的时候往往思路就在注释中, 如果对源码中一个类很迷惑, 可以直接看该类的头部注释往往注明了该类的作用.
最后说点
到此, 真的很想大叫一声宣泄一下, Glide 源码就像一座山, 一座高峰, 你必须沉住气, 慢慢的解读, 要不然稍不留神就会掉入代码的海洋, 迷失方向. 回头看看, 你不得不感叹正式由于 Glide 源码中成千上万行的代码, 才造就了这样一个强大的框架. 最后, 也非常感谢您阅读我的文章, 文章中如果有错误, 请大家给我提出来, 大家一起学习进步, 如果觉得我的文章给予你帮助, 也请给我一个喜欢和关注, 同时也欢迎访问我的个人博客 https://www.maoqitian.com .
参考链接
Gldie 文档 http://bumptech.github.io/glide/
来源: https://juejin.im/post/5c6a3a386fb9a049f9131e95