Glide源码解析3:缓存相关

问题

  1. Glide分别有哪些缓存
  2. Glide在什么时候会从缓存中读取数据
  3. Glide在什么时候会从存入缓存

Glide有哪些缓存

  1. 弱引用缓存ActiveResources
  2. Lru缓存LruResourceCache
  3. 硬盘缓存DiskLruCache

如何从缓存中拉取数据

内存缓存

在SingleRequest发起任务时

  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) {
    
    //根据model、自定义的signature、宽高、变形等参数生成一个key用于识别这次load请求
    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);
      }
    }

    // 有缓存就直接回调onResourceReady了,都不用放线程池处理
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }
  
  
private EngineResource<?> loadFromMemory(
  EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }
    
    //从ActiveResources中获取
    EngineResource<?> active = loadFromActiveResources(key);
    if (active != null) {
      return active;
    }
    
    //从MemoryCache中获取,默认是LruResourceCache
    EngineResource<?> cached = loadFromCache(key);
    if (cached != null) {
      return cached;
    }
    
    return null;
}

//根据key从activeResources对象中获取,它是一个Map<Key, ResourceWeakReference> 
private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = activeResources.get(key);
    if (active != null) {
        //active的被引用数+1,在想要release的时候只有等于0才会真的去释放
      active.acquire();
    }

    return active;
}

private EngineResource<?> loadFromCache(Key key) {
    //以remove的方式拿数据,即拿到了会直接从LruCache中删除并去除该对象
    EngineResource<?> cached = getEngineResourceFromCache(key);
    if (cached != null) {
        //同样被引用数+1
      cached.acquire();
      //若从lruCache中获取到了所需数据,则
      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;
  }

总结:

  1. 在正式通过线程池开启任务之前,会先尝试从内存拉取缓存
  2. 缓存的key为EngineKey,包含了model、size、options等多个属性
  3. 先从ActiveResource中获取,它存的是当前正在使用的Resource
  4. 再尝试从LruResourceCache中获取,它存的是当前未使用的Resource(因为它所包含的Resource一使用就会被remove掉插入ActiveResource中)

硬盘缓存

在DecodeJob执行时,有一个获取下一步任务的方法

//DecodeJob.java
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:
        // Skip loading from source if the user opted to only retrieve the resource from cache.
        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
      case SOURCE:
      case FINISHED:
        return Stage.FINISHED;
      default:
        throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}

//RESOURCE_CACHE和DATA_CACHE就对应的是转换过大小后的最终图片和源图片
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);
    }
}

所以他们分别对应了ResourceCacheGenerator和DataCacheGenerator,以ResourceCacheGenerator为例

public boolean startNext() {
    //遍历最终可能生成的Resource类,如Bitmap、GifDrawable、BitmapDrawable
    while (modelLoaders == null || !hasNextModelLoader()) {
      resourceClassIndex++;
      if (resourceClassIndex >= resourceClasses.size()) {
        sourceIdIndex++;
        if (sourceIdIndex >= sourceIds.size()) {
          return false;
        }
        resourceClassIndex = 0;
      }

      Key sourceId = sourceIds.get(sourceIdIndex);
      Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
      Transformation<?> transformation = helper.getTransformation(resourceClass);
      
      //与内存的key类似,通过宽高等属性生成一个硬盘缓存key
      currentKey =
          new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
        //从DiskLruCache中获取对应文件
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    //获取缓存成功,则接下来通过ModelLoader去加载
    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;
  }

我们可以在Glide初始化时找到输入为File的ModelLoader

//Glide.java
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())

它会把通过ByteBufferUtil把File转化为ByteBuffer

//ByteBufferFileLoader.java
public void loadData(
        @NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
      ByteBuffer result;
      try {
        //这里实现的File到ByteBuffer的转换
        result = ByteBufferUtil.fromFile(file);
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
        }
        callback.onLoadFailed(e);
        return;
      }

      callback.onDataReady(result);
}

//ByteBufferUtil.java
public static ByteBuffer fromFile(@NonNull File file) throws IOException {
    RandomAccessFile raf = null;
    FileChannel channel = null;
    try {
      long fileLength = file.length();
      // See #2240.
      if (fileLength > Integer.MAX_VALUE) {
        throw new IOException("File too large to map into memory");
      }
      // See b/67710449.
      if (fileLength == 0) {
        throw new IOException("File unsuitable for memory mapping");
      }

      raf = new RandomAccessFile(file, "r");
      channel = raf.getChannel();
      return channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength).load();
    } finally {
      if (channel != null) {
        try {
          channel.close();
        } catch (IOException e) {
          // Ignored.
        }
      }
      if (raf != null) {
        try {
          raf.close();
        } catch (IOException e) {
          // Ignored.
        }
      }
    }
  }

而DataCacheGenerator的实现方式也是通过DiskLruCache实现的,但是它的key则没有ResourceCacheGenerator的key构建那么复杂,只是根据传入的model和signature合并起来获得key而已,所以就不再赘述了。

以上就是缓存的读取部分

何时往缓存中保存数据

内存缓存

在数据被转化成最终的Drawable数据之后,EncodeJob会收到onEngineJobComplete回调

//EncodeJob.java
public synchronized void onEngineJobComplete(
      EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
    // A null resource indicates that the load failed, usually due to an exception.
    if (resource != null && resource.isMemoryCacheable()) {
        //把数据存入activeResources中
      activeResources.activate(key, resource);
    }

    jobs.removeIfCurrent(key, engineJob);
}

而对于LruResourceCache,它的put方法会在一个Resource被调用release时调用

//EngineResource.java
void release() {
    boolean release = false;
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) {
        release = true;
      }
    }
    if (release) {
        //若被引用数为0,那么调用Engine的onResourceReleased
      listener.onResourceReleased(key, this);
    }
}

public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    //从activeResources移除对应的弱引用
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
        //存到LRUResourceCache中
      cache.put(cacheKey, resource);
    } else {
      resourceRecycler.recycle(resource);
    }
  }

所以内存缓存的存储时机如下

  1. 一个数据加载成功之后,会存入ActiveResource中
  2. 一个Resource的被引用数为0之后,会存入LruResourceCache中

硬盘缓存

源数据Data缓存

在SourceGenerator成功加载出数据之后,会回调到onDataReady

//SourceGenerator.java
public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
        //若要保存Data数据,就在此处了
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      //重新触发调用EncodeJob执行同一个DecodeJob
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
  }
  
  //DecodeJob.java
public void reschedule() {
    //调用到decode之前,把runReason改为SWITCH_TO_SOURCE_SERVICE
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    callback.reschedule(this);
}

//EncodeJob.java
public void reschedule(DecodeJob<?> job) {
    //这里再去重新执行DecodeJob
    getActiveSourceExecutor().execute(job);
}

//DeocdeJob.java
private void runWrapped() {
    switch (runReason) {
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        //这次走这里,此时的currentGenerator还是SourceGenerator
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    }
  }
  
  //SourceGenerator.java
  @Override
  //转了一圈,重新回到了这里
  public boolean startNext() {
    //dataToCache在本小节最初就赋值了,就是我们得到的源数据
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      //这里就是关键方法cacheData
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }
  
  
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
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      //存入DiskLruCache中
      helper.getDiskCache().put(originalKey, writer);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(
            TAG,
            "Finished encoding source to cache"
                + ", key: "
                + originalKey
                + ", data: "
                + dataToCache
                + ", encoder: "
                + encoder
                + ", duration: "
                + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    //当前要运行的generator改为DataCacheGenerator,之后就会从这个对象里拉取数据回调到onLoadDataReady
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

上面就是源数据Data最终存储到DiskLruCache中的过程,会在SourceGenerator加载完成源数据(如InputStream)后保存

最终数据Resource缓存

最终数据Resource会在DecodeJob加载完成后回调成功时生成
即onDataFetchReady之后会调用到decode的步骤

//DecodeJob.java
private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
        //这里去通过Decoder把InputStream转化为BitmapDrawable之后
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
        //回调最终数据resource已生成
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }
  
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {

    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        //注意这里就是去编码数据,把数据缓存到DiskLruCache
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
    onEncodeComplete();
}

void encode(DiskCacheProvider diskCacheProvider, Options options) {
      GlideTrace.beginSection("DecodeJob.encode");
      try {
        //getDiskCache就是DiskLruCache了,它会通过各种encoder去写数据
        //而Encoder其实也和Decoder类似是通过Glide初始化时实现的
        diskCacheProvider
            .getDiskCache()
            .put(key, new DataCacheWriter<>(encoder, toEncode, options));
      } finally {
        toEncode.unlock();
        GlideTrace.endSection();
      }
}

Glide自带的Encoder类

//Glide.java
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
.append(GifDrawable.class, new GifDrawableEncoder())
.append(Bitmap.class, bitmapEncoder)

总结

Glide分别有哪些缓存

分别有

  1. 弱引用ActiveResource(正在使用的)
  2. 内存缓存LruCache
  3. 硬盘缓存DiskLruCache(包括Data和Resource)

Glide在什么时候会从缓存中读取数据

一次加载开始

  1. 从ActiveResource拉取Resource
  2. 再从LruCache中拉取Resource
  3. 最后从DiskLruCache中拉取
  4. 以上都没有,自己去加载

Glide在什么时候会从存入缓存

  1. 一个Resource加载完成后直接存入ActiveResource
  2. 一个Resource完全没有target使用后存入LruCache
  3. 成功加载出Data(如InputStream)后,存入DiskLruCache的Data
  4. decode加载结束生成了最终Resource后,存入DiskLruCache的Resource
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值