Glide 缓存机制

Posted qianxiangs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Glide 缓存机制相关的知识,希望对你有一定的参考价值。

Glide 缓存机制

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

  //退出app,在次进入,弱引用已被清理,从LurCached缓存加载
  EngineResource<?> cached = loadFromCache(key);
  if (cached != null) 
    if (VERBOSE_IS_LOGGABLE) 
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    
    return cached;
  

  return null;

 

 Glide 在加载图片前会查询弱引用中是否有图片,如果有会直接加载,如果没有就会去找LruCached.

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

    // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
    // deadlock.
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  

如果LruCached 也不存在,会查找磁盘缓存,如果磁盘缓存也没有才会进行网络请求,Glide 网络请求类HttpUrlFetcher 

 private <R> LoadStatus waitForExistingOrStartNewJob(
      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,
      EngineKey key,
      long startTime) 

    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) 
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) 
        logWithTimeAndKey("Added to existing load", startTime, key);
      
      return new LoadStatus(cb, current);
    

    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);

    DecodeJob<R> decodeJob =
        decodeJobFactory.build(
            glideContext,
            model,
            key,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            onlyRetrieveFromCache,
            options,
            engineJob);

    jobs.put(key, engineJob);

    engineJob.addCallback(cb, callbackExecutor);
    //读磁盘缓存
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) 
      logWithTimeAndKey("Started new load", startTime, key);
    
    return new LoadStatus(cb, engineJob);
  

EngineJob 是一个线程对象,调用 strat 方法开启一个线程池,执行EngineJob线程的run 方法

class DecodeJob<R>
    implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable,
        Comparable<DecodeJob<?>>,
        Poolable 

 

  public synchronized void start(DecodeJob<R> decodeJob) 
    this.decodeJob = decodeJob;
    GlideExecutor executor =
        decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
    //线程池
    executor.execute(decodeJob);
  

 在run 方法中runWapped()方法 -->DecodeJob

@Override
  public void run() 
     ......
    try 
      if (isCancelled) 
        notifyFailed();
        return;
      
      runWrapped();
     catch (CallbackException e) 
     
     catch (Throwable t) 
     
      if (stage != Stage.ENCODE) 
        throwables.add(t);
        notifyFailed();
      
  
     finally 

      if (localFetcher != null) 
        localFetcher.cleanup();
      
      GlideTrace.endSection();
    
  

 在runWraoped()方法中分为三个swith 分别 INITIALIZE SWITCH_TO_SOURCE_SERVICE DECODE_DATA 默认走第一个INITIALIZE

/**
   * 读磁盘缓存
   */
  private void runWrapped() 
    switch (runReason) 
      case INITIALIZE:
        stage = getNextStage(Stage.INITIALIZE);
        currentGenerator = getNextGenerator();
        runGenerators();
        break;
      case SWITCH_TO_SOURCE_SERVICE:
        runGenerators();
        break;
      case DECODE_DATA:
        decodeFromRetrievedData();
        break;
      default:
        throw new IllegalStateException("Unrecognized run reason: " + runReason);
    
  

在第一个 INITIALIZE 方法中调用 runGenerators方法  -->DecodeJob

 private void runGenerators() 
    currentThread = Thread.currentThread();
    startFetchTime = LogTime.getLogTime();
    boolean isStarted = false;
    while (!isCancelled
        && currentGenerator != null
                //currentGenerator.startNext()
        && !(isStarted = currentGenerator.startNext())) 
      stage = getNextStage(stage);
      currentGenerator = getNextGenerator();

      if (stage == Stage.SOURCE) 
        reschedule();
        return;
      
    
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) 
      notifyFailed();
    

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  

在runGenerators 方法中调用currentGenerator.startNext() 这个currentGenerator 调的是 ResourceCacheGenerator startNext 方法

public boolean startNext() 
  //添加到LruCache中
    List<Key> sourceIds = helper.getCacheKeys();
    if (sourceIds.isEmpty()) 
      return false;
    
    List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
    if (resourceClasses.isEmpty()) 
      if (File.class.equals(helper.getTranscodeClass())) 
        return false;
      
      throw new IllegalStateException(
          "Failed to find any load path from "
              + helper.getModelClass()
              + " to "
              + helper.getTranscodeClass());
    
    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);
      // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
      // we only run until the first one succeeds, the loop runs for only a limited
      // number of iterations on the order of 10-20 in the worst case.
      //key
      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;
  

//添加到LruCache中 然后在创建存储key 通过 key 找到磁盘存储的文件 然后调用  loadData.fetcher.loadData(helper.getPriority(), this);   -->ByteBufferFileLoader

  @Override
    public void loadData(
        @NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) 
      ByteBuffer result;
      try 
        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);
    

然后调用onDataReady  -->ResourceCacheGenerator

@Override
  public void onDataReady(Object data) 
    cb.onDataFetcherReady(
        sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey);
  

 然后调用 onDataFetcherReady -->DecodeJob

public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) 
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    if (Thread.currentThread() != currentThread) 
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
     else 
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try 
         //转码
        decodeFromRetrievedData();
       finally 
        GlideTrace.endSection();
      
    
  

然后调用  decodeFromRetrievedData();  -->DecodeJob

  private void decodeFromRetrievedData() 
    if (Log.isLoggable(TAG, Log.VERBOSE)) 
      logWithTimeAndKey(
          "Retrieved data",
          startFetchTime,
          "data: "
              + currentData
              + ", cache key: "
              + currentSourceKey
              + ", fetcher: "
              + currentFetcher);
    
    Resource<R> resource = null;
    try 
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
     catch (GlideException e) 
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    
    if (resource != null) 
      notifyEncodeAndRelease(resource, currentDataSource);
     else 
      runGenerators();
    
  

 然后调用 decodeFromData 解码到 Resource Resource 不等于空调用 notifyEncodeAndRelease  -->DecodeJob

 private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) 
    if (resource instanceof Initializable) 
      ((Initializable) resource).initialize();
    

    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()) 
        deferredEncodeManager.encode(diskCacheProvider, options);
      
     finally 
      if (lockedResource != null) 
        lockedResource.unlock();
      
    
    // Call onEncodeComplete outside the finally block so that it's not called if the encode process
    // throws.
    onEncodeComplete();
  

然后调用 notifyComplete -->DecodeJob 

 private void notifyComplete(Resource<R> resource, DataSource dataSource) 
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
  

 然后调用 onResourceReady --> EngineJob

 @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) 
    synchronized (this) 
      this.resource = resource;
      this.dataSource = dataSource;
    
    notifyCallbacksOfResult();
  

 然后调用 notifyCallbacksOfResult- -> EngineJob 

  @Synthetic
  void notifyCallbacksOfResult() 
    ResourceCallbacksAndExecutors copy;
    Key localKey;
    EngineResource<?> localResource;
    synchronized (this) 
      stateVerifier.throwIfRecycled();
      if (isCancelled) 
        // TODO: Seems like we might as well put this in the memory cache instead of just recycling
        // it since we've gotten this far...
        resource.recycle();
        release();
        return;
       else if (cbs.isEmpty()) 
        throw new IllegalStateException("Received a resource without any callbacks to notify");
       else if (hasResource) 
        throw new IllegalStateException("Already have resource");
      
      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);
      // Hold on to resource for duration of our callbacks below so we don't recycle it in the
      // middle of notifying if it synchronously released by one of the callbacks. Acquire it under
      // a lock here so that any newly added callback that executes before the next locked section
      // below can't recycle the resource before we call the callbacks.
      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);

      localKey = key;
      localResource = engineResource;
    

    //保存弱引用
    engineJobListener.onEngineJobComplete(this, localKey, localResource);

    for (final ResourceCallbackAndExecutor entry : copy) 
        //CallResourceReady 线程run方法
      entry.executor.execute(new CallResourceReady(entry.cb));
    
    decrementPendingCallbacks();
  

  @SuppressWarnings("WeakerAccess")
  @Synthetic
  synchronized void incrementPendingCallbacks(int count) 
    Preconditions.checkArgument(isDone(), "Not yet complete!");
    if (pendingCallbacks.getAndAdd(count) == 0 && engineResource != null) 
      engineResource.acquire();
    
  

executor 线程池调用 CallResourceReady 线程run方法 -->CallResourceReady

  private class CallResourceReady implements Runnable 

    private final ResourceCallback cb;

    CallResourceReady(ResourceCallback cb) 
      this.cb = cb;
    

    @Override
    public void run() 
      // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
      // (b/136032534).
      synchronized (cb.getLock()) 
        synchronized (EngineJob.this) 
          if (cbs.contains(cb)) 
            // Acquire for this particular callback.
            engineResource.acquire();
            callCallbackOnResourceReady(cb);
            removeCallback(cb);
          
          decrementPendingCallbacks();
        
      
    
  

然后调用 callCallbackOnResourceReady  - -> EngineJob 

  @SuppressWarnings("WeakerAccess")
  @Synthetic
  @GuardedBy("this")
  void callCallbackOnResourceReady(ResourceCallback cb) 
    try 
      // This is overly broad, some Glide code is actually called here, but it's much
      // simpler to encapsulate here than to do so at the actual call point in the
      // Request implementation.
      cb.onResourceReady(engineResource, dataSource);
     catch (Throwable t) 
      throw new CallbackException(t);
    
  

 然后调用 onResourceReady -->SingleRequest

  @Override
  public void onResourceReady(Resource<?> resource, DataSource dataSource) 
    stateVerifier.throwIfRecycled();
    Resource<?> toRelease = null;
    try 
      synchronized (requestLock) 
        loadStatus = null;
        if (resource == null) 
         
          onLoadFailed(exception);
          return;
        

        Object received = resource.get();
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) 
          toRelease = resource;
          this.resource = null;
         
          onLoadFailed(exception);
          return;
        

        if (!canSetResource()) 
          toRelease = resource;
          this.resource = null;
          // We can't put the status to complete before asking canSetResource().
          status = Status.COMPLETE;
          return;
        

        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      
     finally 
      if (toRelease != null) 
        engine.release(toRelease);
      
    
  

 然后调用 onResourceReady -->SingleRequest 

  @GuardedBy("requestLock")
  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) 
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (glideContext.getLogLevel() <= Log.DEBUG) 
      
    

    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);
      
     finally 
      isCallingCallbacks = false;
    

    notifyLoadSuccess();
  

     target 为 ImageViewTarget

 target.onResourceReady(result, animation);  --> ImageViewTarget

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) 
    if (transition == null || !transition.transition(resource, this)) 
      setResourceInternal(resource);
     else 
      maybeUpdateAnimatable(resource);
    
  

然后调用 setResourceInternal --> ImageViewTarget

 private void setResourceInternal(@Nullable Z resource) 
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);
    maybeUpdateAnimatable(resource);
  
 @Override
  protected void setResource(@Nullable Drawable resource) 
    Log.d(TAG, "setResource: -----"+resource);
    view.setImageDrawable(resource);
  

这就是图片加载的整体流程,首先Glide会查询弱引用,如果弱引用没有,会查找LruCached,如果LruCached也没有会查找DiskLruCache 磁盘缓存,如果磁盘缓存也没有,才会进行网络请求,请求到数据会同步到弱引用,LruCached,DiskLruCache,这就是整体策略,这还要注意,如果磁盘缓存有图片,就不会网络请求,会同步到弱引用和LruCache 中.

以上是关于Glide 缓存机制的主要内容,如果未能解决你的问题,请参考以下文章

Android-Glide的缓存机制

Glide 缓存机制

Glide 缓存机制

Glide 缓存机制

Glide 缓存机制

Glide 缓存机制及源码