Android 進(jìn)階學(xué)習(xí)(十四) Glide源碼學(xué)習(xí)(三) DecodeJob 分析

Glide 中的DecodeJob 得工作其實(shí)是比較亂的,看的我真是不要不要的,真想說 read the f**king source code
今天繼續(xù)昨天的DecodeJob 來分析,

先來捋一捋整個(gè)圖片第一次加載流程,這里我們將它分為4步

1.根據(jù)當(dāng)前的url 寬 高 還有其他信息,去查找已經(jīng)在磁盤上面按照?qǐng)D片的寬高緩存好的圖片,并一層一層向上返回

2.如果按照寬高沒有找到,則嘗試尋找沒有寬高的原圖,如果存在原圖,那么將這個(gè)原圖按照寬高獲取,并一層一層向上返回

3.如果原圖也沒有,則去下載這個(gè)圖片,

4.圖片下載完后,根據(jù)是否可以在磁盤緩存,如果可以則執(zhí)行第2步,否則直接向上返回

工作流程已經(jīng)知道了,繼續(xù)看一下關(guān)鍵的類

EngineJob

EngineJob 可以作為橋梁通知上層接口,同時(shí)還可以作為線程選擇的調(diào)度

ResourceCacheGenerator

ResourceCacheGenerator 是一個(gè)根據(jù)指定的寬高去加載磁盤緩存的工具類,

DataCacheGenerator

DataCacheGenerator 是一個(gè)根據(jù)原圖去加載指定寬高的工具類

SourceGenerator

SourceGenerator是一個(gè)處理緩存和開啟下載圖片的工具類

DecodeHelper

DecodeHelper 是 一個(gè)幫助我們獲取一些信息和實(shí)施下載的工具類

知道了這些再去看代碼會(huì)輕松的很多,

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

先看一下DecodeJob 的類的繼承關(guān)系,需要注意的是他實(shí)現(xiàn)了Runnable 接口,這樣我們就知道改如何他看的內(nèi)部方法的執(zhí)行順序了,還是從上一篇的構(gòu)建DecodeJob開始分析

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

從代碼上看到創(chuàng)建了一個(gè)DecodeJob ,并使用engineJob.start ,

EngineJob.start

 public synchronized void start(DecodeJob<R> decodeJob) {
   this.decodeJob = decodeJob;
   GlideExecutor executor = decodeJob.willDecodeFromCache()
       ? diskCacheExecutor
       : getActiveSourceExecutor();
   executor.execute(decodeJob);
 }

看到他做了一個(gè)判斷,用來選擇線程池來執(zhí)行DecodeJob ,從字面的意思應(yīng)該是配置有關(guān)系,意思應(yīng)該是是否可以從緩存解碼,如果可以則使用磁盤緩存線程池,否則選擇下載線程池,既然是執(zhí)行DecodeJob 這個(gè)Runnable ,那么直接去看一下他的Runnable

DecodeJob.run

 @Override
 public void run() {
   // This should be much more fine grained, but since Java's thread pool implementation silently
   // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
   // that something is failing.
   GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
   // Methods in the try statement can invalidate currentFetcher, so set a local variable here to
   // ensure that the fetcher is cleaned up either way.
   DataFetcher<?> localFetcher = currentFetcher;
   try {
     if (isCancelled) {
       notifyFailed();
       return;
     }
     runWrapped();
   } catch (CallbackException e) {
     // If a callback not controlled by Glide throws an exception, we should avoid the Glide
     // specific debug logic below.
     throw e;
   } catch (Throwable t) {
     // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
     // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
     // are however ensuring that our callbacks are always notified when a load fails. Without this
     // notification, uncaught throwables never notify the corresponding callbacks, which can cause
     // loads to silently hang forever, a case that's especially bad for users using Futures on
     // background threads.
     if (Log.isLoggable(TAG, Log.DEBUG)) {
       Log.d(TAG, "DecodeJob threw unexpectedly"
           + ", isCancelled: " + isCancelled
           + ", stage: " + stage, t);
     }
     // When we're encoding we've already notified our callback and it isn't safe to do so again.
     if (stage != Stage.ENCODE) {
       throwables.add(t);
       notifyFailed();
     }
     if (!isCancelled) {
       throw t;
     }
     throw t;
   } finally {
     // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
     // close in all cases anyway.
     if (localFetcher != null) {
       localFetcher.cleanup();
     }
     GlideTrace.endSection();
   }
 }

在run方法中先判斷了是否已經(jīng)取消此次請(qǐng)求,其實(shí)我在最開始閱讀的時(shí)候在這里就有一個(gè)疑問,既然加載圖片,那么在開始加載前就判斷是否取消有什么意義,難道寫代碼的時(shí)候Glide.with(context).load(url).into(imageView),然后下一行代碼直接取消?否則最開始判斷取消的意義又在哪里,這個(gè)問題我們先記一下,后面我們?cè)诜治龃a的過程中會(huì)解答

繼續(xù)分析run 方法,判斷了是否取消后就執(zhí)行了 runWrapped(); 方法

不過這里我們需要定義一下,此次加載是我么首次加載這張圖片,不存在內(nèi)存緩存,也不存在磁盤緩存,

DecodeJob.runWrapped

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

可以看到他是根據(jù)狀態(tài)來判斷執(zhí)行的哪一個(gè)方法,DecodeJob 在初始化的時(shí)候?qū)顟B(tài)設(shè)置為INITIALIZE,那么此時(shí)我們就INITIALIZE分支,記住現(xiàn)在的狀態(tài)

狀態(tài) INITIALIZE狀態(tài)

      stage = getNextStage(Stage.INITIALIZE);
      currentGenerator = getNextGenerator();
      runGenerators();

在INITIALIZE 狀態(tài)下執(zhí)行了上面的方法 ,去看getNextStage(Stage.INITIALIZE);

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

在這個(gè)根據(jù) diskCacheStrategy 來判斷是否使用磁盤緩存寬高緩存,這個(gè)是在構(gòu)建Glide時(shí)配置的緩存設(shè)置,默認(rèn)使用DiskCacheStrategy.AUTOMATIC ,返回的是true

DiskCacheStrategy.AUTOMATIC

 public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
   @Override
   public boolean isDataCacheable(DataSource dataSource) {
     return dataSource == DataSource.REMOTE;
   }

   @Override
   public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
       EncodeStrategy encodeStrategy) {
     return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
         || dataSource == DataSource.LOCAL)
         && encodeStrategy == EncodeStrategy.TRANSFORMED;
   }

   @Override
   public boolean decodeCachedResource() {
     return true;
   }

   @Override
   public boolean decodeCachedData() {
     return true;
   }
 };

此時(shí)狀態(tài)變更為RESOURCE_CACHE ,繼續(xù)執(zhí)行g(shù)etNextGenerator 方法

狀態(tài) RESOURCE_CACHE

DecodeJob.getNextGenerator

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

返回的是一個(gè)ResourceCacheGenerator,并將它賦值給currentGenerator ,然后執(zhí)行runGenerators方法

DecodeJob.runGenerators

 private void runGenerators() {
   currentThread = Thread.currentThread();
   startFetchTime = LogTime.getLogTime();
   boolean isStarted = false;
   while (!isCancelled && currentGenerator != null
       && !(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.
 }

這里的判斷就比較抽象了,

此時(shí)就執(zhí)行了我們上面所描述的第1步

沒有被取消,currentGenerator 不為空,在getNextGenerator,我們得到的了一個(gè)ResourceCacheGenerator,所以這個(gè)條件也成立,currentGenerator.startNext() 的意思就是是否找到有相應(yīng)寬高的磁盤緩存,首次加載肯定是沒有的,那么這個(gè)條件的否定命題也是成立的,

執(zhí)行完第1步后更改狀態(tài),執(zhí)行第2步

當(dāng)前狀態(tài)RESOURCE_CACHE,此時(shí)就會(huì)再次更換狀態(tài)stage = getNextStage(stage);將狀態(tài)變更為DATA_CACHE,同時(shí)也會(huì)判斷是否可以保存原圖,在默認(rèn)的DiskCacheStrategy.AUTOMATIC 配置中是true,此時(shí) currentGenerator = getNextGenerator();返回了一個(gè)DataCacheGenerator,由于是第一次加載所以這里currentGenerator.startNext 返回同樣是false,繼續(xù)執(zhí)行whild循環(huán)

同理執(zhí)行第3步,

當(dāng)前狀態(tài)為DATA_CACHE,我們同樣還是通過stage = getNextStage(stage);將狀態(tài)變更為SOURCE,將currentGenerator變更為SourceGenerator,此時(shí)

 if (stage == Stage.SOURCE) {
      reschedule();
      return;
    }

當(dāng)前狀態(tài)變更到了SOURCE,跳出循環(huán),執(zhí)行reschedule

DecodeJob.reschedule

 @Override
 public void reschedule() {
   runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
   callback.reschedule(this);
 }

在reschedule 方法中將runReason 變更為 SWITCH_TO_SOURCE_SERVICE,同時(shí)調(diào)用callback.reschedule 方法,在DecodeJob的初始化方法中發(fā)現(xiàn)這個(gè)callback就是 EngineJob,我去看一下他的reschedule方法

EngineJob.reschedule

 @Override
 public void reschedule(DecodeJob<?> job) {
   // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
   // up.
   getActiveSourceExecutor().execute(job);
 }

這里就比較簡(jiǎn)單了,重新選擇一個(gè)線程池來執(zhí)行DecodeJob 這個(gè)Runnable,同樣也解釋了我們?cè)谏厦鏇]有說清楚兩個(gè)問題,
1.在engineJob.start(decodeJob);中我們說他是根據(jù)一個(gè)配置來選擇所需的線程池,從上面的代碼其實(shí)是可以理解的,磁盤緩存和下載是在不同的線程池中工作的,
2.那就是為什么在run 方法的最開始就判斷是否已經(jīng)取消加載圖片,原因就是存在線程的調(diào)度,這個(gè)run方法不一定只執(zhí)行一次

調(diào)用完線程后,將會(huì)重新執(zhí)行DecodeJob.run 方法,我們先標(biāo)記一下狀態(tài)

runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;

stage=SOURCE

currentGenerator=SourceGenerator

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

此時(shí)再執(zhí)行run方法后 就會(huì)執(zhí)行runWrapped 的SWITCH_TO_SOURCE_SERVICE 分支,執(zhí)行 runGenerators 方法,此時(shí)就會(huì)執(zhí)行SourceGenerator的下載方法,看到這里想必整個(gè)加載的流程大家心里面已經(jīng)有一個(gè)差不多的概念了,

這個(gè)里面涉及到了三個(gè)關(guān)鍵的類 ResourceCacheGenerator DataCacheGenerator SourceGenerator 他們的具體工作流程,我們來看一下,主要是看他們的startNext 的方法

ResourceCacheGenerator.startNext

@Override
 public boolean startNext() {
   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.
     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;
 }

關(guān)于ResourceCacheGenerator 這個(gè)類的表述,其中還涉及到DecodeHelper的一些使用,我也是從網(wǎng)上看了一些文章,https://blog.csdn.net/nbsp22/article/details/80651648 這一篇文章講的就非常不錯(cuò),還有這個(gè)邏輯雖然我能看懂,但是讓我描述出來就有點(diǎn)繞了,我也從他的文章中copy 一下他的描述

此時(shí)通過decodeHelper拿到的sourceIds就是[GlideUrl,ObjectKey]荣暮,如果通過注冊(cè)的信息找不到此時(shí)的key,表明glide本身還不支持這種方式,因此調(diào)用結(jié)束毕荐。顯然此時(shí)是支持的告匠,接下來是通過helper的getRegisteredResourceClasses獲取resourceClass信息易猫,這里大致是glide所支持的資源類信息歌懒,也就是能夠進(jìn)行decode的昼伴。這里它存放的是[GifDrawable,Bitmap,BitmapDrawable]匾旭。因此接下來進(jìn)入第一個(gè)的while循環(huán):

1.由resourceClasses和sourceIds組成的一個(gè)正交關(guān)系,迭代每一組圃郊。
2.迭代開始前价涝,若modelLoaders為空或者size為0,則繼續(xù)迭代進(jìn)入步驟3持舆,否則循環(huán)結(jié)束色瘩。
3/循環(huán)中,檢測(cè)是否已經(jīng)全部迭代完成逸寓,如果還有居兆,則進(jìn)入步驟4,否則循環(huán)結(jié)束竹伸。
4.對(duì)每一組泥栖,獲取相應(yīng)的緩存Key對(duì)象,根據(jù)緩存key去diskcache中查找緩存文件勋篓,查找成功吧享,則通過getModelLoaders獲取當(dāng)前的modelLoaders信息,繼續(xù)執(zhí)行循環(huán)譬嚣,進(jìn)入步驟2钢颂。

從這里我們可以看出這個(gè)while循環(huán)的作用就是找到modelLoaders信息,如果沒找到有效的拜银,則循環(huán)結(jié)束殊鞭,方法塊正交組迭代完成之后,startNext方法結(jié)束盐股,方法返回false钱豁,交給下一個(gè)Generator去處理。如果能夠找到疯汁,則執(zhí)行下一個(gè)while循環(huán)牲尺。這個(gè)循環(huán)相對(duì)簡(jiǎn)單一些,就是根據(jù)上一個(gè)while循環(huán)查找到的modelLoaders幌蚊,進(jìn)行遍歷谤碳,只要有一個(gè)對(duì)應(yīng)的fetcher能夠處理,則startNext返回true溢豆,表明此時(shí)這個(gè)generator已經(jīng)能夠處理本次請(qǐng)求蜒简,所以也不會(huì)再交給其他的generator對(duì)應(yīng)的fetcher去處理了。

在我們此時(shí)的情景中漩仙,ResourceCacheGenerator是無法處理本次請(qǐng)求的搓茬,所以犹赖,交給下一個(gè)Generator去處理,也就是DataCacheGenerator的startNext卷仑。

DataCacheGenerator .startNext

@Override
 public boolean startNext() {
   while (modelLoaders == null || !hasNextModelLoader()) {
     sourceIdIndex++;
     if (sourceIdIndex >= cacheKeys.size()) {
       return false;
     }

     Key sourceId = cacheKeys.get(sourceIdIndex);
     // PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
     // and the actions it performs are much more expensive than a single allocation.
     @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
     Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
     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;
 }

如果你看了上面關(guān)于ResourceCacheGenerator的描述,那么再看這里就比較簡(jiǎn)單了,我們來說一下他們之間有什么不同,在ResourceCacheGenerator 與 DataCacheGenerator 都是利用key 從緩存中匹配的,但是在ResourceCacheGenerator 中的key的創(chuàng)建利用了寬高而DataCacheGenerator 中則沒有,可見ResourceCacheGenerator 是獲取的是響應(yīng)寬高的資源,而DataCacheGenerator 是獲取的原圖,在獲取原圖成功后再根據(jù)寬高去獲取相應(yīng)數(shù)據(jù),

第一次加載這里肯定也是沒有的,繼續(xù)去下載

SourceGenerator .startNext

 @Override
 public boolean startNext() {
   if (dataToCache != null) {
     Object data = dataToCache;
     dataToCache = null;
     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;
 }

首次執(zhí)行 startNext 方法中的dataToCache 肯定是null,所有不會(huì)走加載緩存的方法,而是進(jìn)入while中執(zhí)行循環(huán)下載圖片,具體的下載過程是在loadData.fetcher.loadData(helper.getPriority(), this);這個(gè)方法中執(zhí)行的,我們就不分析了,來說一下下載后他干了什么,下載成功后會(huì)回調(diào)onDataReady 這個(gè)方法

SourceGenerator.onDataReady

 @Override
 public void onDataReady(Object data) {
   DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
   if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
     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.
     cb.reschedule();
   } else {
     cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
         loadData.fetcher.getDataSource(), originalKey);
   }
 }

判斷了一下是否可以磁盤換粗,如果可以將dataToCache 設(shè)置為data用于數(shù)據(jù)緩存,繼續(xù)執(zhí)行線程調(diào)度,我們?cè)谏厦娣治鲞^了,如果線程調(diào)度之后肯定還是會(huì)走SourceGenerator.startNext 方法,此時(shí)dataToCache 不為null,

SourceGenerator.startNext

   if (dataToCache != null) {
     Object data = dataToCache;
     dataToCache = null;
     cacheData(data);
   }

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

這里是走了緩存數(shù)據(jù),然后創(chuàng)建一個(gè)DataCacheGenerator(原始圖片解碼器) 去執(zhí)行他的解碼,

SourceGenerator.cacheData

 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 = new DataCacheKey(loadData.sourceKey, helper.getSignature());
     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();
   }

   sourceCacheGenerator =
       new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
 }

到這里整個(gè)過程就結(jié)束了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末峻村,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锡凝,更是在濱河造成了極大的恐慌粘昨,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窜锯,死亡現(xiàn)場(chǎng)離奇詭異张肾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)锚扎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門吞瞪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人工秩,你說我怎么就攤上這事尸饺。” “怎么了助币?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵浪听,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我眉菱,道長(zhǎng)迹栓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任俭缓,我火速辦了婚禮克伊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘华坦。我一直安慰自己愿吹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布惜姐。 她就那樣靜靜地躺著犁跪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歹袁。 梳的紋絲不亂的頭發(fā)上坷衍,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音条舔,去河邊找鬼枫耳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛孟抗,可吹牛的內(nèi)容都是我干的迁杨。 我是一名探鬼主播钻心,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼仑最!你這毒婦竟也來了扔役?” 一聲冷哼從身側(cè)響起帆喇,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤警医,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后坯钦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體预皇,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年婉刀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吟温。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡突颊,死狀恐怖鲁豪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情律秃,我是刑警寧澤爬橡,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站棒动,受9級(jí)特大地震影響糙申,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜船惨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一柜裸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粱锐,春花似錦疙挺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至海雪,卻和暖如春锦爵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奥裸。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工险掀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人湾宙。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓樟氢,卻偏偏與公主長(zhǎng)得像冈绊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埠啃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容