美文网首页
Glide最新源码解析(五)-缓存策略-内存缓存

Glide最新源码解析(五)-缓存策略-内存缓存

作者: 烧伤的火柴 | 来源:发表于2019-10-08 11:26 被阅读0次

    介绍

    内存缓存我的理解就是在内存中持久化一份数据,当下次需要的时候直接取出来使用。对于Android设备就两块内存,一块是磁盘,一块是虚拟机中的堆内存,因为堆内存空间大而且是线程共享的。Glide中利用这两块内存做了极大化的缓存,提高数据的访问速度。所以总结缓存策略就是:内存缓存和磁盘缓存。不过在两种缓存的基础上又做出了优化方法。
    说明一点:这里缓存管理的都是Resource<T>对象,该对象是对原生资源(baitmap,drawable,gif)的一种包装
    EngineResource<T>实现Resource接口,对资源的引用计算,采用引用计数法,当资源被引用的时候计数器+1,当引用不在使用的时候计数器-1,当计数=0的时候,回调释放资源接口,高层模块决定释放的资源怎么处理。EngineResource是一个代理模式(智能引用)代理Resource对象。

    内存缓存

    Glide在内存缓存中做出的优化是,加入了弱引用缓存,内存缓存和复用池(bitmapArrayPool,byteArrayPool)

    弱引用缓存-ActiveResources

    ActiveResources缓存ResourceWeakReference对象,该对象弱引用EngineResource对象。
    既然是内存缓存(map管理)就一定离不开增,删,查动作,我们看一下这几个动作的处理方式。
    先看一下ActiveResources的结构

    final class ActiveResources {
    private final boolean isActiveResourceRetentionAllowed;
      private final Executor monitorClearedResourcesExecutor;//监视被清除资源的线程池
      @VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
      private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
    
      private ResourceListener listener;
    
      private volatile boolean isShutdown;//是否终止线程
    

    1.在构造的时候会启动一个核心线程,一直运行监视弱引用队列中的引用对象被gc回收掉,被回收掉的对象就调用cleanupActiveReference方法,从缓存中移除引用,并且调用 listener.onResourceReleased(ref.key, newResource);

      ActiveResources(
          boolean isActiveResourceRetentionAllowed, Executor monitorClearedResourcesExecutor) {
        this.isActiveResourceRetentionAllowed = isActiveResourceRetentionAllowed;
        this.monitorClearedResourcesExecutor = monitorClearedResourcesExecutor;
    
        monitorClearedResourcesExecutor.execute(
            new Runnable() {
              @Override
              public void run() {
                cleanReferenceQueue();
              }
            });
      }
      void cleanReferenceQueue() {
        while (!isShutdown) {//1
          try {
            ResourceWeakReference ref = (ResourceWeakReference) resourceReferenceQueue.remove();
            cleanupActiveReference(ref);
            ...
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
          }
        }
      }
    
      void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
        synchronized (this) {
          activeEngineResources.remove(ref.key);
    
          if (!ref.isCacheable || ref.resource == null) {
            return;
          }
        }
    
        EngineResource<?> newResource =
            new EngineResource<>(
                ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
        listener.onResourceReleased(ref.key, newResource);//1.释放资源
      }
    

    这里有一个疑问:resourceReferenceQueue是一个阻塞队列,remove方法的时候,没有数据的时候会调用
    lock.wait(timeout);挂起线程等待唤醒,所以我感觉不需要while死循环(注释1)。后来请教了Alan老师,才恍然大悟,这个队列是所有ResourceWeakReference对象共享的一个队列,所以需要一直循环监听对象回收的情况。 我们看一下释放的资源怎么处理?实现的地方在Engine中

    public class Engine
        implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
    ...
        @Override
      public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
        activeResources.deactivate(cacheKey);
        if (resource.isMemoryCacheable()) {//支持缓存,放入缓存中
          cache.put(cacheKey, resource);
        } else {//不支持缓存就回收掉
          resourceRecycler.recycle(resource);
        }
      }
    ...
    

    释放掉的资源放入内存缓存中。

    2.添加资源

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

    注意这里new ResourceWeakReference的时候,传入的是公用的一个resourceReferenceQueue队列。我们看一下这个类的结构

    static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
        @SuppressWarnings("WeakerAccess")
        @Synthetic
        final Key key;
    
        @SuppressWarnings("WeakerAccess")
        @Synthetic
        final boolean isCacheable;
    
        @Nullable
        @SuppressWarnings("WeakerAccess")
        @Synthetic
        Resource<?> resource;
    
        @Synthetic
        @SuppressWarnings("WeakerAccess")
        ResourceWeakReference(
            @NonNull Key key,
            @NonNull EngineResource<?> referent,
            @NonNull ReferenceQueue<? super EngineResource<?>> queue,
            boolean isActiveResourceRetentionAllowed) {
          super(referent, queue);
          this.key = Preconditions.checkNotNull(key);
          this.resource =
              referent.isMemoryCacheable() && isActiveResourceRetentionAllowed
                  ? Preconditions.checkNotNull(referent.getResource())
                  : null;
          isCacheable = referent.isMemoryCacheable();
        }
    
        void reset() {
          resource = null;
          clear();
        }
      }
    

    这里使用强引用引用了resource对象,以供后续资源释放的时候使用

    2.移除资源

     synchronized void deactivate(Key key) {
        ResourceWeakReference removed = activeEngineResources.remove(key);
        if (removed != null) {
          removed.reset();
        }
      }
    

    3.获取资源

      synchronized EngineResource<?> get(Key key) {
        ResourceWeakReference activeRef = activeEngineResources.get(key);
        if (activeRef == null) {
          return null;
        }
    
        EngineResource<?> active = activeRef.get();
        if (active == null) {
          cleanupActiveReference(activeRef);
        }
        return active;
      }
    

    总结

    ActiveResources缓存的是Resource的弱引用,并且监听Resource被GC回收掉,被回收掉的弱引用使用listener.onResourceReleased(ref.key, newResource);处理,从map主动移除的弱引用都调用reset()释放掉引用的resource

    内存缓存

    先看一下内存缓存的接口结构

    /** 内存缓存中添加和移除资源的接口 */
    public interface MemoryCache {
      /** 无论什么时候当bitmap从缓存中移出去的时候回调的接口(这指的移除指的是当内存溢出的时候,要溢出资源来容纳其他资源) */
      interface ResourceRemovedListener {
        void onResourceRemoved(@NonNull Resource<?> removed);
      }
    
      /** 查询缓存中所有内容的大小,单位是byte */
      long getCurrentSize();
    
      /** 内存最大的容量 */
      long getMaxSize();
    
      /**
    
       * 调整内存大小
       * <p>If the size multiplier causes the size of the cache to be decreased, items will be evicted
       * until the cache is smaller than the new size.
       *
       * @param multiplier A size multiplier >= 0.
       */
      void setSizeMultiplier(float multiplier);
    
      /**
    
       * 根据key移除资源,返回移除的资源
       * @param key The key.
       */
      @Nullable
      Resource<?> remove(@NonNull Key key);
    
      /**
       * 添加资源
       * @param key The key to retrieve the bitmap.
       * @param resource The {@link com.bumptech.glide.load.engine.EngineResource} to store.
       * @return The old value of key (null if key is not in map).
       */
      @Nullable
      Resource<?> put(@NonNull Key key, @Nullable Resource<?> resource);
    
      /**
       * 资源移除回调
       * @param listener The listener.
       */
      void setResourceRemovedListener(@NonNull ResourceRemovedListener listener);
    
      /** 清空内存中所有的数据 */
      void clearMemory();
    
      /**
       * 根据不同的级别调整内存大小
       * @param level This integer represents a trim level as specified in {@link
       *     android.content.ComponentCallbacks2}.
       */
      void trimMemory(int level);
    }
    

    接口的实现类有两个一个是MemoryCacheAdapter 适配不适用内存缓存的时候,一个是LruResourceCache
    我们主要分析LruResourceCache.java,顾名思义采用的是LRU算法缓存的资源。

     public class LruResourceCache extends LruCache<Key, Resource<?>> implements MemoryCache {
              private ResourceRemovedListener listener;
              ....
         @Override
        public void setResourceRemovedListener(@NonNull ResourceRemovedListener listener) {
          this.listener = listener;
        }
    
          @Override
        protected void onItemEvicted(@NonNull Key key, @Nullable Resource<?> item) {
            if (listener != null && item != null) {
            listener.onResourceRemoved(item);//1资源从内存中移除
          }
        }
    ....
     }
    

    这里采用的是类适配器模式,LruCache中的put get方法去适配MemoryCache 接口中定义的抽象方法。Glide中的LruCache没有采用Android系统的LruCache类,而是自己实现了一个LruCache类,其中主要区别是Glide中的onItemEvicted方法只有在put和trimToSize时候调用。
    注释1处资源从内存中移出去的回调,实现该回调的地方是Engine类。

    public class Engine
        implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
     
       ...
       @Override
      public void onResourceRemoved(@NonNull final Resource<?> resource) {
        //回收资源
        resourceRecycler.recycle(resource);
      }
      ...
    }
    

    ResourceRecycler类中会按照顺序安全的回收每一个资源,即调用resource.recycle();方法,我们默认用的是BitmapResuource实例,我们看一下BitmapResuource中的recycle方法。

    public class BitmapResource implements Resource<Bitmap>, Initializable {
    ...
    @Override
      public void recycle() {
        bitmapPool.put(bitmap);
      }
    ...
    }
    

    被回收掉的bitmap放到了复用池中。小记:内存缓存中当内存溢出的时候,会清理资源腾出空间,以满足其他资源的加入,清理掉的资源会被放入复用池中。

    相关文章

      网友评论

          本文标题:Glide最新源码解析(五)-缓存策略-内存缓存

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