美文网首页
Glide缓存

Glide缓存

作者: 竹叶儿青 | 来源:发表于2020-06-07 17:16 被阅读0次

Glide缓存分为内存缓存和磁盘缓存。

1. 内存缓存

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

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

  @Nullable
  private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }

    return active;
  }

  private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    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, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
    }
    return result;
  }

可以看到,load方法在生成key之后,首先调用loadFromMemory方法从内存中加载,如果加载失败,则调用waitForExistingOrStartNewJob方法开启线程去加载。
在loadFromMemory方法中,首先判断是否开启缓存,默认是开启的,如果在skipMemoryCache中传入true,则跳过内存缓存:

 private boolean isCacheable = true;

 @NonNull
  @CheckResult
  public T skipMemoryCache(boolean skip) {
    if (isAutoCloneEnabled) {
      return clone().skipMemoryCache(true);
    }

    this.isCacheable = !skip;
    fields |= IS_CACHEABLE;

    return selfOrThrowIfLocked();
  }

如果开启了内存缓存,则在loadFromMemory方法中先调用了loadFromActiveResources方法,加载使用WeakReference弱引用的仍在使用中的资源,并对资源调用acquire方法进行加一操作:

private final ActiveResources activeResources;

final class ActiveResources {
  private final boolean isActiveResourceRetentionAllowed;
  private final Executor monitorClearedResourcesExecutor;
  @VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
...
 synchronized void activate(Key key, EngineResource<?> resource) {
    ResourceWeakReference toPut =
        new ResourceWeakReference(
            key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);

    ResourceWeakReference removed = activeEngineResources.put(key, toPut);
    if (removed != null) {
      removed.reset();
    }
  }

@VisibleForTesting
  static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
...
}
}

 synchronized void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    ++acquired;
  }

如果没有找到active resource,则调用loadFromCache方法加载,调用cache#acquire方法,然后调用activeResources#activate将获取到的资源放入active resource中。
这里的cache是LruCache,使用Lru算法,Glide#with方法调用getRetriever->Glide#get->checkAndInitializeGlide->initializeGlide->找到一行代码:Glide glide = builder.build(applicationContext);,查看build方法:

@NonNull
  Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    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(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory);

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        isLoggingRequestOriginsEnabled,
        isImageDecoderEnabledForBitmaps);
  }
}

public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache{...}

这里memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());就是使用Lru,LruResourceCache继承自LruCache,在LruCache中cache是用LinkedHashMap实现的,accessOrder参数为true,表示允许按访问次数排序。

/** An LRU in memory cache for {@link com.bumptech.glide.load.engine.Resource}s. */
public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {...}

**
 * A general purpose size limited cache that evicts items using an LRU algorithm. By default every
 * item is assumed to have a size of one. Subclasses can override {@link #getSize(Object)}} to
 * change the size on a per item basis.
 *
 * @param <T> The type of the keys.
 * @param <Y> The type of the values.
 */
public class LruCache<T, Y> {
  private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
...}

从缓存中或者从网络下载得到资源后就调用cb#onResourceReady将资源回传,这里的cb就是callback,SingleRequest对象。

总的来说,内存缓存会尝试从activeResources对象中获取正在使用的资源,如果失败则尝试从cache对象中获取,activeResources是使用弱引用的HashMap,cache是使用Lru算法的LinkedHashMap。

私以为,设计2层内存缓存为了 1:使用弱引用存储正在使用的资源,可以降低内存溢出的风险;因为Lru算法的LinkedHashMap是强引用,会导致内存泄漏;2:在从cache获得资源后,将资源从cache中移除,加入activeResources,减轻了Lru的压力,减少trimToSize的次数。(参考郭霖的Glide缓存机制的评论)

2. 磁盘缓存

磁盘缓存通过diskCacheStrategy方法设置,策略有5种:

  • DiskCacheStrategy.ALL
    缓存原始数据和处理后的数据,即DATA和RESOURCE的结合
  • DiskCacheStrategy.NONE
    不缓存任何数据
  • DiskCacheStrategy.DATA
    缓存获取到的原始未解码数据
  • DiskCacheStrategy.RESOURCE
    缓存解码后的数据
  • DiskCacheStrategy.AUTOMATIC
    默认选项,自动判断策略

磁盘缓存使用DiskLruCache实现,实现思路大致和内存缓存一致,针对DiskCacheStrategy.ALL的情况,则是将下载完成的原始数据和解码之后的数据存入缓存。

相关文章

网友评论

      本文标题:Glide缓存

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