美文网首页
Glide源码之缓存机制

Glide源码之缓存机制

作者: Jack921 | 来源:发表于2021-09-29 21:04 被阅读0次

    在上一篇文章中,我们简要的讲了Glide加载一张网络图片的基本流程,整个流程有点长,也有点多,很多东西没有细讲。所以会对Glide一些重要的东西单独写文章讲解,以便大家对Glide细节了解更深。假如说一个优秀的网络图片加载框架最重要的是什么,它的缓存机制无疑是最重要的,它决定框架加载的资源统筹。在看这一篇文章假如没有看Glide源码之基本流程加载建议先这这一篇,让你对整个流程有大概的了解。

    Glide缓存设计

    Glide分成内存缓存和磁盘缓存两个:

    • 内存缓存:基于基于弱引用和LruCache(先看弱引用有无缓存,再看LruCache有无缓存)
    • 磁盘缓存:基于DiskLruCache进行封装(当内存没有时,看磁盘有无,有就获取没有只网络获取)

    大致流程

    内存缓存->磁盘缓存->网络加载

    当网络加载完,再把图片缓存加载到内存和磁盘

    加载流程

    首先我们看一下有关上一篇的加载代码:

    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;
    
        //生成资源的key
        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);
          }
        }
    
        // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
        // deadlock.
        cb.onResourceReady(
            memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
        return null;
      }
    

    我们先看看keyFactory.buildKey()方法:

    EngineKey buildKey(
          Object model,
          Key signature,
          int width,
          int height,
          Map<Class<?>, Transformation<?>> transformations,
          Class<?> resourceClass,
          Class<?> transcodeClass,
          Options options) {
        return new EngineKey(
            model, signature, width, height, transformations, resourceClass, transcodeClass, options);
      }
      
    //EngineKey类源码
    class EngineKey implements Key {
      private final Object model;
      private final int width;
      private final int height;
      private final Class<?> resourceClass;
      private final Class<?> transcodeClass;
      private final Key signature;
      private final Map<Class<?>, Transformation<?>> transformations;
      private final Options options;
      private int hashCode;
    
      EngineKey(
          Object model,
          Key signature,
          int width,
          int height,
          Map<Class<?>, Transformation<?>> transformations,
          Class<?> resourceClass,
          Class<?> transcodeClass,
          Options options) {
          
        this.model = Preconditions.checkNotNull(model);
        this.signature = Preconditions.checkNotNull(signature, "Signature must not be null");
        this.width = width;
        this.height = height;
        this.transformations = Preconditions.checkNotNull(transformations);
        this.resourceClass = Preconditions.checkNotNull(resourceClass, "Resource class must not be null");
        this.transcodeClass = Preconditions.checkNotNull(transcodeClass, "Transcode class must not be null");
        this.options = Preconditions.checkNotNull(options);
      }
    
      @Override
      public boolean equals(Object o) {
        if (o instanceof EngineKey) {
          EngineKey other = (EngineKey) o;
          return model.equals(other.model)
              && signature.equals(other.signature)
              && height == other.height
              && width == other.width
              && transformations.equals(other.transformations)
              && resourceClass.equals(other.resourceClass)
              && transcodeClass.equals(other.transcodeClass)
              && options.equals(other.options);
        }
        return false;
      }
    
      @Override
      public int hashCode() {
        if (hashCode == 0) {
          hashCode = model.hashCode();
          hashCode = 31 * hashCode + signature.hashCode();
          hashCode = 31 * hashCode + width;
          hashCode = 31 * hashCode + height;
          hashCode = 31 * hashCode + transformations.hashCode();
          hashCode = 31 * hashCode + resourceClass.hashCode();
          hashCode = 31 * hashCode + transcodeClass.hashCode();
          hashCode = 31 * hashCode + options.hashCode();
        }
        return hashCode;
      }
    
      省略代码....    
        
    }
      
    

    EngineKey很简单,就是存贮宽高,签名等参数生成对象,然后重写equals()和hashCode()方法,让他们不同宽高都返回不同的key,只有参数一样才会返回一样的key,确保资源唯一性。
    上面很简单,接着我们再看看获取内存缓存的方法loadFromMemory()代码如下:

    private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
        if (!isMemoryCacheable) {
          return null;
        }
    
        //看内存的弱引用缓存有无对应key,有则返回
        EngineResource<?> active = loadFromActiveResources(key);
        if (active != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
          }
          return active;
        }
    
        //看内存的Lrucache缓存有无对应的key,有则返回
        EngineResource<?> cached = loadFromCache(key);
        if (cached != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
          }
          return cached;
        }
    
        return null;
      }
    

    弱引用缓存loadFromActiveResources():

    private EngineResource<?> loadFromActiveResources(Key key) {
        //从根据key从弱引用对象获取数据
        EngineResource<?> active = activeResources.get(key);
        if (active != null) {
          active.acquire();
        }
    
        return active;
      }
      
    //get()方法源码: 
    synchronized EngineResource<?> get(Key key) {
        //从activeEngineResources取出弱引用对象
        ResourceWeakReference activeRef = activeEngineResources.get(key);
        if (activeRef == null) {
          return null;
        }
        
        //获取弱引用对象数据缓存
        EngineResource<?> active = activeRef.get();
        if (active == null) {
        //假如等于null,就证明被垃圾回收机制回收
          cleanupActiveReference(activeRef);
        }
        return active;
      }
    

    这里的activeEngineResources是什么呢,其实就是一个hashmap对象:

    final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

    所以弱引用对象是放在HashMap那里的,然后拿到对象看看资源有没有被垃圾回收机制回收,假如被回收了,就调用cleanupActiveReference()方法,源码如下:

    //cleanupActiveReference()方法源码:  
    void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
        synchronized (this) {
          //删除该弱引用对象
          activeEngineResources.remove(ref.key);
    
          //判断缓存是否可用
          if (!ref.isCacheable || ref.resource == null) {
            return;
          }
        }
        
        //恢复EngineResource,其中这个对象封装了图片资源
        EngineResource<?> newResource = new EngineResource<>(ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
        //回调,该listener为Engine对象
        listener.onResourceReleased(ref.key, newResource);
      }  
      
    

    接着会回调Engine的onResourceReleased()方法,代码如下:

    public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
        //删除弱引用
        activeResources.deactivate(cacheKey);
        //设置了缓存
        if (resource.isMemoryCacheable()) {
          //将弱引用数据存在内存缓存LruCache
          cache.put(cacheKey, resource);
        } else {
          resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
        }
    }
    

    也就是说弱引用没有了,就放在内存缓存LruCache,然后下一步在内存缓存加载,看有没有,就可以从LruCache获取到。

    loadFromCache()

    假如弱引用没有的话就会调loadFromCache()从LruCache获取,现在方法源码:

    private EngineResource<?> loadFromCache(Key key) {
        //从LruCache获取图片资源
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
          //引用加一
          cached.acquire();
          activeResources.activate(key, cached);
        }
        return cached;
    }
    
    //图片资源具体方法
    private EngineResource<?> getEngineResourceFromCache(Key key) {
        //从LruCache获取图片资源并且删除缓存
        Resource<?> cached = cache.remove(key);
    
        final EngineResource<?> result;
        if (cached == null) {
          result = null;
        } else if (cached instanceof EngineResource) {
          result = (EngineResource<?>) cached;
        } else {
          result = new EngineResource<>(cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
        }
        return result;
    }
    
    

    获取图片资源之后就是调 activeResources.activate(key, cached),把他放在弱引用:

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

    这样就保证了弱引用没有,LruCache有,就直接把资源存入弱引用同时删除LruCache,同时也让图片不会被LruCache算法回收掉。

    假如弱引用缓存和内存缓存都没有的时候,就要看磁盘缓存或者网络加载

    waitForExistingOrStartNewJob()
    private <R> LoadStatus waitForExistingOrStartNewJob(.....) {
    
       //省略代码.....
    
        //创建EngineJob对象,加载图片
        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);
    
        return new LoadStatus(cb, engineJob);
      }
      
    public synchronized void start(DecodeJob<R> decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
        executor.execute(decodeJob);
    }  
    
    

    可以看到,用线程池开线程来加载图片,会调用DecodeJob的run()方法:

    public void run() {
        //重点运行了runWrapped(),进行加载
        runWrapped();
    }
    
    private void runWrapped() {
        switch (runReason) {
          case INITIALIZE:
            //获取加载状态
            stage = getNextStage(Stage.INITIALIZE);
            //获取加载对应的Generator
            currentGenerator = getNextGenerator();
            //执行相应的Generator
            runGenerators();
            break;
          case SWITCH_TO_SOURCE_SERVICE:
            runGenerators();
            break;
          case DECODE_DATA:
            decodeFromRetrievedData();
            break;
          default:
            throw new IllegalStateException("Unrecognized run reason: " + runReason);
        }
    }
      
    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);
        }
    }  
      
    

    这里分别有三个Generator,对应的功能功能如下:

    1. ResourceCacheGenerator (处理解码之后的资源缓存Generator)
    2. DataCacheGenerator (处理源数据的缓存G的enerator)
    3. SourceGenerator (处理网络获取数据资源的Generator)
      最后的runGenerators(),就是针对Generator进行运行处理,代码如下:
    private void runGenerators() {
        currentThread = Thread.currentThread();
        startFetchTime = LogTime.getLogTime();
        boolean isStarted = false;
        while (!isCancelled
            && currentGenerator != null
            //startNext()方法就是执行Generator的方法
            && !(isStarted = currentGenerator.startNext())) {
          stage = getNextStage(stage);
          currentGenerator = getNextGenerator();
    
          if (stage == Stage.SOURCE) {
            reschedule();
            return;
          }
        }
       
        if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
          notifyFailed();
        }
    }
    
    ResourceCacheGenerator

    我们先来看看ResourceCacheGenerator的startNext()方法吧:

    public boolean startNext() {
        List<Key> sourceIds = helper.getCacheKeys();
        
        //省略代码...
    
        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());
    
        //通过key获取在磁盘获取资源        
        cacheFile = helper.getDiskCache().get(currentKey);
    
        if (cacheFile != null) {
           sourceKey = sourceId;
           //该modeLoaders的类型为File类型的
           modelLoaders = helper.getModelLoaders(cacheFile);
           modelLoaderIndex = 0;
        }
    
    
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
          //获取加载数据相应的ModelLoader    
          ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
    
           //生成加载器,这里生成的是ByteBufferFileLoader
          loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
    
          if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
            stdarted = true;
            //调用ByteBufferFileLoader的loadData进行数据加载
            loadData.fetcher.loadData(helper.getPriority(), this);
          }
        }
    
        return started;
      }
    

    ByteBufferFileLoader又有一个内部类ByteBufferFetcher,最终会调用ByteBufferFetcher的loadData()方法:

    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
        ByteBuffer result;
        try {
           result = ByteBufferUtil.fromFile(file);
           callback.onDataReady(result);
        } catch (IOException e) {
           callback.onLoadFailed(e);
        }
    }
    
    public static ByteBuffer fromFile(@NonNull File file) throws IOException {
        RandomAccessFile raf = null;
        FileChannel channel = null;
        try {
          long fileLength = file.length();
          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) {
              
            }
          }
          if (raf != null) {
            try {
              raf.close();
            } catch (IOException e) {
            
            }
          }
        }
      }
    
    

    可以看到,他们是通过ByteBufferUtil来把文件转成数据,底层是通过RandomAccessFile和FileChannel来转化的,然后通过ResourceCacheGenerator的onDataReady进行回调,看回调代码:

    ResourceCacheGenerator.onDataReady()

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

    对应这DecodeJob的onDataFetcherReady()方法:

    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;
        this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);
    
        if (Thread.currentThread() != currentThread) {
          runReason = RunReason.DECODE_DATA;
          callback.reschedule(this);
        } else {
          GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
          try {
            //处理获取的数据
            decodeFromRetrievedData();
          } finally {
            GlideTrace.endSection();
          }
        }
    }
    
    private void decodeFromRetrievedData() {
        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, isLoadingFromAlternateCacheKey);
        } else {
          runGenerators();
        }
    }
    
    private void notifyEncodeAndRelease(
          Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
        //省略代码
    
        //继续回调把资源返回给上一层
        notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey);
    
        stage = Stage.ENCODE;
        try {
          if (deferredEncodeManager.hasResourceToEncode()) {
            //资源缓存到磁盘
            deferredEncodeManager.encode(diskCacheProvider, options);
          }
        } finally {
          if (lockedResource != null) {
            lockedResource.unlock();
          }
        }
       
        onEncodeComplete();
    }
    
    private void notifyComplete(Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
        setNotifiedOrThrow();
        callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
    }
    
    void encode(DiskCacheProvider diskCacheProvider, Options options) {
          GlideTrace.beginSection("DecodeJob.encode");
          try {
            //将图片资源缓存到资源磁盘
            diskCacheProvider.getDiskCache().put(key,new DataCacheWriter<>(encoder, toEncode, options));
          } finally {
            toEncode.unlock();
            GlideTrace.endSection();
          }
    }
    
    

    回调的流程可以参考我的上一篇文章Glide源码之基本流程加载

    DataCacheGenerator

    接着看DataCacheGenerator,先看startNext()代码:

     public boolean startNext() {
        while (modelLoaders == null || !hasNextModelLoader()) {
          sourceIdIndex++;
          if (sourceIdIndex >= cacheKeys.size()) {
            return false;
          }
    
          Key sourceId = cacheKeys.get(sourceIdIndex);
          //生成缓存key
          Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
          //根据key从磁盘资源中获取资源
          cacheFile = helper.getDiskCache().get(originalKey);
          if (cacheFile != null) {
            this.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;
    }
    

    DataCacheGenerator的startNext()和ResourceCacheGenerator大致相似,唯一不同的是生成key的参数不一样,原生图片不需要宽高,配置等参数,其他基本相同,没啥讲的

    SourceGenerator的startNext(),先看核心代码:
    public boolean startNext() {
         
        //省略代码...
    
        startNextLoad(loadData);
         
        //省略代码...
    }
    
    private void startNextLoad(final LoadData<?> toStart) {
        loadData.fetcher.loadData(
            helper.getPriority(),
            new DataCallback<Object>() {
              @Override
              public void onDataReady(@Nullable Object data) {
                if (isCurrentRequest(toStart)) {
                  onDataReadyInternal(toStart, data);
                }
              }
    
              @Override
              public void onLoadFailed(@NonNull Exception e) {
                if (isCurrentRequest(toStart)) {
                  onLoadFailedInternal(toStart, e);
                }
              }
            });
      }
    

    而这里的loadData对应的是HttpUrlFetcher的loadData()方法,

    public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
        long startTime = LogTime.getLogTime();
        try {
          InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
          callback.onDataReady(result);
        } catch (IOException e) {
          callback.onLoadFailed(e);
        } finally {
          
        }
    }
    
    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException {
        //省略代码...
    
        //配置连接参数
        urlConnection = buildAndConfigureConnection(url, headers);
    
        try {
          //于服务器连接
          urlConnection.connect();
          stream = urlConnection.getInputStream();
        } catch (IOException e) {
          throw new HttpException(
              "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e);
        }
    
        if (isCancelled) {
          return null;
        }
    
        final int statusCode = getHttpStatusCodeOrInvalid(urlConnection);
        if (isHttpOk(statusCode)) {
          //连接成功,获取输入流
          return getStreamForSuccessfulRequest(urlConnection);
        } else if (isHttpRedirect(statusCode)) {
          String redirectUrlString = urlConnection.getHeaderField(REDIRECT_HEADER_FIELD);
          URL redirectUrl;
          try {
            redirectUrl = new URL(url, redirectUrlString);
          } catch (MalformedURLException e) {
            throw new HttpException("Bad redirect url: " + redirectUrlString, statusCode, e);
          }
          cleanup();
          return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else if (statusCode == INVALID_STATUS_CODE) {
          throw new HttpException(statusCode);
        } else {
          try {
            throw new HttpException(urlConnection.getResponseMessage(), statusCode);
          } catch (IOException e) {
            throw new HttpException("Failed to get a response message", statusCode, e);
          }
        }
    }
    
    private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
          throws HttpException {
        try {
          if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
            int contentLength = urlConnection.getContentLength();
            stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
          } else {
            //核心代码,底层默认通过HttpURLConnection获取数据流
            stream = urlConnection.getInputStream();
          }
        } catch (IOException e) {
          throw new HttpException(
              "Failed to obtain InputStream", getHttpStatusCodeOrInvalid(urlConnection), e);
        }
        return stream;
      }
    
    

    从源码可以看出SourceGenerator是默认通过HttpURLConnection进行网络访问获取数据。接着看SourceGenerator的onDataReady()调用的onDataReadyInternal方法:

    void onDataReadyInternal(LoadData<?> loadData, Object data) {
        DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
        //判断有无设置缓存
        if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
          //假如设置缓存
          dataToCache = data;
          cb.reschedule();
        } else {
          cb.onDataFetcherReady(
              loadData.sourceKey,
              data,
              loadData.fetcher,
              loadData.fetcher.getDataSource(),
              originalKey);
        }
      }
    

    开启了磁盘缓存会调用DecodeJob的reschedule()方法:

    public void reschedule() {
        runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
        callback.reschedule(this);
    }
    
    public void reschedule(DecodeJob<?> job) {
        //用线程池开启线程运行
        getActiveSourceExecutor().execute(job);
    }
    
    public void run() {
        GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
        DataFetcher<?> localFetcher = currentFetcher;
        try {
          if (isCancelled) {
            notifyFailed();
            return;
          }
          //重新调用run方法
          runWrapped();
        } catch (CallbackException e) {
          throw e;
        } catch (Throwable t) {
          if (stage != Stage.ENCODE) {
            throwables.add(t);
            notifyFailed();
          }
          if (!isCancelled) {
            throw t;
          }
          throw t;
        } finally {
          if (localFetcher != null) {
            localFetcher.cleanup();
          }
          GlideTrace.endSection();
        }
    }
    
    

    可以发现,有重新调用的runWrapped()方法了,接着又是重新执行SourceGenerator的startNext()方法,只不过这次的执行不同了,

    public boolean startNext() {
            
         //第二次进入
        //dataToCache不等于null,为之前下载的原始图片    
        if (dataToCache != null) {
          Object data = dataToCache;
          dataToCache = null;
          //调用disklrucache缓存
          cacheData(data);
        }
        
        //因为有网络下载的图片资源,所以不为空,会用SourceCacheGenerator获取图片资源
        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;
            startNextLoad(loadData);
          }
        }
        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());
          //生成key      
          originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
           //DiskLrucache保存图片
          helper.getDiskCache().put(originalKey, writer);
          
        } finally {
          loadData.fetcher.cleanup();
        }
        //生成DataCacheGenerator对象,用来加载刚保存的磁盘缓存
        sourceCacheGenerator =
            new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
      }
     
    

    用DiskLrucache保存图片图片后,会用DataCacheGenerator去加载资源。

    小结:
    Glide中首先会读取转换后的图片的缓存,然后再读取原始图片的缓存。但是存储的时候恰恰相反,首先存储的是原始图片的缓存,再存储转换后的图片。

    相关文章

      网友评论

          本文标题:Glide源码之缓存机制

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