[1] Universal-image-loader数据接口和缓存实现策略

1,590 阅读4分钟

数据缓存

图片数据的起始位置是远端的 数据服务器 ,直到被加载到内存中才可以展示到客户端界面的View上。为了加快数据处理速度,需要在每一步对处理过的数据进行保存,再次需要时直接使用节省时间,是为缓存。

数据缓存.png

缓存策略

数据缓存需要提供合适的缓存策略,原因有二:

  1. 空间是有限的:APP分配到的内存大小和在文件系统中占据的空间大小依赖于操作系统的策略,都是有限的;
  2. 空间不足时,需要对现有的数据进行删除,数据被再次用到的几率和频次是不同的;

缓存分类

依据上面的图,按照缓存的位置不同可以分为下面几个:

  1. 网络缓存:也可以称为云缓存,一般需要从云服务运营商处购买服务,例如:CDN;
  2. 磁盘缓存:从云端下载文件到本地文件系统,保存在文件系统中;
  3. 内存缓存:数据在使用前需要加载到内存中,相对文件系统内存空间更小;

图中还有一个部分 客户端网络框架,通常网络框架也会提供文件系统缓存和内存缓存,但是他们本质上是在同一个APP运行实例中,内存和文件系统空间是共享的,没有必要同时使用,这里要注意做好处理。

缓存工具

下面每个部分都有一些工具,例如 BaseImageDownloaderBaseImageDecoder 在框架中起到重要的连接作用,同时也可以作为独立的工具类提供图片数据下载和加载服务。

文件缓存

加载到文件系统

image.png

  • BaseImageDownloader

    Universal-image-loader提供 BaseImageDownloader 支持把多种数据来源的图片资源转存到缓存目录中:

    private boolean downloadImage() throws IOException {
       // 获取数据流
       InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
       if (is == null) {
          L.e(ERROR_NO_IMAGE_STREAM, memoryCacheKey);
          return false;
       } else {
          try {
             // 保存到缓存目录
             return configuration.diskCache.save(uri, is, this);
          } finally {
             IoUtils.closeSilently(is);
          }
       }
    }
    
    @Override
    public InputStream getStream(String imageUri, Object extra) throws IOException {
       switch (Scheme.ofUri(imageUri)) {
          case HTTP:
          case HTTPS:
             return getStreamFromNetwork(imageUri, extra);
          case FILE:
             return getStreamFromFile(imageUri, extra);
          case CONTENT:
             return getStreamFromContent(imageUri, extra);
          case ASSETS:
             return getStreamFromAssets(imageUri, extra);
          case DRAWABLE:
             return getStreamFromDrawable(imageUri, extra);
          case UNKNOWN:
          default:
             return getStreamFromOtherSource(imageUri, extra);
       }
    }
    
  • BaseImageDownloader扩展

    BaseImageDownloader为每种资源类型提供了接口 获取Http/Https数据使用的java api提供的网络接口 HttpURLConnection, 如果需要使用其他网络框架例如Okhttp,可以继承 BaseImageDownloader 重新实现 getStreamFromNetwork(), 并为 ImageLoaderConfiguration#downloader 设置一个实例即可。

文件系统缓存策略

文件缓存的类图结构比较简单,提供了两类缓存 无限容量缓存有限容量缓存 。为容量控制提供了两种回收机制。

  1. LimitedAgeDiskCache: 通过缓存时间控制缓存空间回收。
  2. LruDiskCache:使用最近最少使用算法,控制缓存空间回收。 Universal-Image-loader框架默认使用UnlimitedDistCache:ImageLoaderConfiguration#diskCache

image.png

内存缓存

加载到内存

ImageLoaderConfiguration#decoder 提供的默认值是 BaseImageDecoder,图片处理参数 decodingOptions 来自配置参数 DisplayImageOptions#decodingOptions

image.png

内存缓存策略

image.png

上图为MemoryCache模块的类图,橙色区域为核心部分。框架运行时使用的内存缓存策略由ImageLoaderConfiguration#memoryCache参数指定,默认使用:LruMemoryCache

  • 两个基础方案

    1. LruMemoryCache: 带有容量控制的LinkedHashMap结构,设置 accessOrder = true,回收策略使用的是最近最少使用。
    2. WeakMemoryCache: 无容量限制的同步HashMap,Bitmap数据使用 弱引用 方式持有图片数据。
  • LimitedMemoryCache 扩展方案

    LimitedMemoryCache 是一组带有容量控制的同步HashMap,Bitmap数据使用 弱引用 方式持有图片数据,总体效果是对 WeakMemoryCache 添加了容量控制的基础上,在空间不足时给定不同的回收策略。这一组类,因为需要给定不同的回收策略,都增加了额外的存储空间,用于辅助查找下一条优先被回收的数据。

    1. FIFOLimitedMemoryCache:先入先出,即队列。当剩余空间不足时,优先回收队头的数据。
    2. LRULimitedMemoryCache:最近最少使用,LinkedHashMap设置 accessOrder = true,计算空间不足时下一条将要被回收的数据。与 LruMemoryCache 算法一致。
    3. UsingFreqLimitedMemoryCache:最少使用,usingCounts 记录每个数据使用的次数,当空间不足时,最少被使用的数据将会被回收。
    4. LargestLimitedMemoryCache: 最大先出,valueSizes 记录每个数据的大小,当空间不足时,占用空间最大的将会被回收。
  • 两个包装类

    1. LimitedAgeMemoryCache:额外增加“时长”控制逻辑,设定最大保存时间(秒数),在取数据时判断是否缓存超过最大允许时间范围,超过就删除。
    2. FuzzyKeyMemoryCache:设置一个 Comparator 实例,在添加数据前,把满足算法的数据删除,然后插入新的数据。