//SingleRequest@Overridepublicsynchronizedvoidbegin(){assertNotCallingCallbacks();Log.d(TAG,"begin: ");stateVerifier.throwIfRecycled();startTime=LogTime.getLogTime();//1. 如果model为null,则调用onLoadFailedif(model==null){if(Util.isValidDimensions(overrideWidth,overrideHeight)){width=overrideWidth;height=overrideHeight;}// Only log at more verbose log levels if the user has set a fallback drawable, because// fallback Drawables indicate the user expects null models occasionally.intlogLevel=getFallbackDrawable()==null?Log.WARN:Log.DEBUG;onLoadFailed(newGlideException("Received null model"),logLevel);return;}//如果正在运行,则抛出异常if(status==Status.RUNNING){thrownewIllegalArgumentException("Cannot restart a running request");}// If we're restarted after we're complete (usually via something like a notifyDataSetChanged// that starts an identical request into the same Target or View), we can simply use the// resource and size we retrieved the last time around and skip obtaining a new size, starting a// new load etc. This does mean that users who want to restart a load because they expect that// the view size has changed will need to explicitly clear the View or Target before starting// the new load.//如果已经完成,则调用onResourceReadyif(status==Status.COMPLETE){Log.d(TAG,"begin: resource"+resource);onResourceReady(resource,DataSource.MEMORY_CACHE);return;}// Restarts for requests that are neither complete nor running can be treated as new requests// and can run again from the beginning.//等待尺寸status=Status.WAITING_FOR_SIZE;//如果设置了overrideWidth和overrideHeight,则调用onSizeReadyif(Util.isValidDimensions(overrideWidth,overrideHeight)){onSizeReady(overrideWidth,overrideHeight);}else{target.getSize(this);}if((status==Status.RUNNING||status==Status.WAITING_FOR_SIZE)&&canNotifyStatusChanged()){target.onLoadStarted(getPlaceholderDrawable());}if(IS_VERBOSE_LOGGABLE){logV("finished run method in "+LogTime.getElapsedMillis(startTime));}}
//SizeDeterminer getSize方法voidgetSize(@NonNullSizeReadyCallbackcb){intcurrentWidth=getTargetWidth();//获取宽intcurrentHeight=getTargetHeight();//获取高if(isViewStateAndSizeValid(currentWidth,currentHeight)){cb.onSizeReady(currentWidth,currentHeight);return;}// We want to notify callbacks in the order they were added and we only expect one or two// callbacks to be added a time, so a List is a reasonable choice.if(!cbs.contains(cb)){cbs.add(cb);}//如果获取为null 添加OnPreDrawListener,当回调时,再次获取宽高if(layoutListener==null){ViewTreeObserverobserver=view.getViewTreeObserver();layoutListener=newSizeDeterminerLayoutListener(this);observer.addOnPreDrawListener(layoutListener);}}
publicsynchronizedvoidonSizeReady(intwidth,intheight){stateVerifier.throwIfRecycled();if(IS_VERBOSE_LOGGABLE){logV("Got onSizeReady in "+LogTime.getElapsedMillis(startTime));}if(status!=Status.WAITING_FOR_SIZE){return;}status=Status.RUNNING;floatsizeMultiplier=requestOptions.getSizeMultiplier();this.width=maybeApplySizeMultiplier(width,sizeMultiplier);this.height=maybeApplySizeMultiplier(height,sizeMultiplier);if(IS_VERBOSE_LOGGABLE){logV("finished setup for calling load in "+LogTime.getElapsedMillis(startTime));}Log.d(TAG,"onSizeReady "+requestOptions.getResourceClass()+",width="+width+",height="+height);loadStatus=engine.load(glideContext,model,requestOptions.getSignature(),//签名this.width,this.height,requestOptions.getResourceClass(),transcodeClass,priority,requestOptions.getDiskCacheStrategy(),//硬盘缓存策略requestOptions.getTransformations(),requestOptions.isTransformationRequired(),requestOptions.isScaleOnlyOrNoTransform(),requestOptions.getOptions(),requestOptions.isMemoryCacheable(),requestOptions.getUseUnlimitedSourceGeneratorsPool(),requestOptions.getUseAnimationPool(),requestOptions.getOnlyRetrieveFromCache(),//只从缓存中获取this,callbackExecutor);// This is a hack that's only useful for testing right now where loads complete synchronously// even though under any executor running on any thread but the main thread, the load would// have completed asynchronously.if(status!=Status.RUNNING){loadStatus=null;}if(IS_VERBOSE_LOGGABLE){logV("finished onSizeReady in "+LogTime.getElapsedMillis(startTime));}}
//等待存在的job或者开启一个新的jobprivate<R>LoadStatuswaitForExistingOrStartNewJob(GlideContextglideContext,Objectmodel,Keysignature,intwidth,intheight,Class<?>resourceClass,Class<R>transcodeClass,Prioritypriority,DiskCacheStrategydiskCacheStrategy,Map<Class<?>,Transformation<?>>transformations,booleanisTransformationRequired,booleanisScaleOnlyOrNoTransform,Optionsoptions,booleanisMemoryCacheable,booleanuseUnlimitedSourceExecutorPool,booleanuseAnimationPool,booleanonlyRetrieveFromCache,ResourceCallbackcb,//回调ExecutorcallbackExecutor,EngineKeykey,longstartTime){//从缓存中获取EngineJob<?>current=jobs.get(key,onlyRetrieveFromCache);if(current!=null){current.addCallback(cb,callbackExecutor);if(VERBOSE_IS_LOGGABLE){logWithTimeAndKey("Added to existing load",startTime,key);}returnnewLoadStatus(cb,current);}EngineJob<R>engineJob=engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);Log.d(TAG," waitForExistingOrStartNewJob resourceClass ="+resourceClass);//创建decodeJobDecodeJob<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);}returnnewLoadStatus(cb,engineJob);}
privatevoidrunWrapped(){switch(runReason){caseINITIALIZE:stage=getNextStage(Stage.INITIALIZE);currentGenerator=getNextGenerator();runGenerators();break;caseSWITCH_TO_SOURCE_SERVICE:runGenerators();break;caseDECODE_DATA:decodeFromRetrievedData();break;default:thrownewIllegalStateException("Unrecognized run reason: "+runReason);}}
privateenumStage{/** * The initial stage. */INITIALIZE,/** * Decode from a cached resource. */RESOURCE_CACHE,/** * Decode from cached source data. */DATA_CACHE,/** * Decode from retrieved source. */SOURCE,/** * Encoding transformed resources after a successful load. */ENCODE,/** * No more viable stages. */FINISHED,}
privateStagegetNextStage(Stagecurrent){switch(current){caseINITIALIZE:returndiskCacheStrategy.decodeCachedResource()?Stage.RESOURCE_CACHE:getNextStage(Stage.RESOURCE_CACHE);caseRESOURCE_CACHE:returndiskCacheStrategy.decodeCachedData()?Stage.DATA_CACHE:getNextStage(Stage.DATA_CACHE);caseDATA_CACHE:// Skip loading from source if the user opted to only retrieve the resource from cache.returnonlyRetrieveFromCache?Stage.FINISHED:Stage.SOURCE;caseSOURCE:caseFINISHED:returnStage.FINISHED;default:thrownewIllegalArgumentException("Unrecognized stage: "+current);}}
privatevoidrunGenerators(){currentThread=Thread.currentThread();startFetchTime=LogTime.getLogTime();booleanisStarted=false;while(!isCancelled&¤tGenerator!=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.}
publicbooleanstartNext(){Log.d(TAG,"startNext");List<Key>sourceIds=helper.getCacheKeys();if(sourceIds.isEmpty()){returnfalse;}List<Class<?>>resourceClasses=helper.getRegisteredResourceClasses();if(resourceClasses.isEmpty()){if(File.class.equals(helper.getTranscodeClass())){returnfalse;}thrownewIllegalStateException("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()){returnfalse;}resourceClassIndex=0;}KeysourceId=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=newResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoopshelper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformation,resourceClass,helper.getOptions());//获取缓存文件cacheFile=helper.getDiskCache().get(currentKey);if(cacheFile!=null){Log.d(TAG,"cacheFile"+cacheFile.getAbsolutePath());sourceKey=sourceId;modelLoaders=helper.getModelLoaders(cacheFile);//获取对应的modelLoadersmodelLoaderIndex=0;}}loadData=null;booleanstarted=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);}}returnstarted;}
//构造函数 调用DecodeHelper的getCacheKeysDataCacheGenerator(DecodeHelper<?>helper,FetcherReadyCallbackcb){this(helper.getCacheKeys(),helper,cb);}// In some cases we may want to load a specific cache key (when loading from source written to// cache), so we accept a list of keys rather than just obtain the list from the helper.DataCacheGenerator(List<Key>cacheKeys,DecodeHelper<?>helper,FetcherReadyCallbackcb){this.cacheKeys=cacheKeys;this.helper=helper;this.cb=cb;}
@OverridepublicbooleanstartNext(){while(modelLoaders==null||!hasNextModelLoader()){sourceIdIndex++;if(sourceIdIndex>=cacheKeys.size()){returnfalse;}KeysourceId=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")KeyoriginalKey=newDataCacheKey(sourceId,helper.getSignature());//获取缓存文件cacheFile=helper.getDiskCache().get(originalKey);if(cacheFile!=null){this.sourceKey=sourceId;modelLoaders=helper.getModelLoaders(cacheFile);modelLoaderIndex=0;}}loadData=null;booleanstarted=false;Log.d(TAG,"modelLoaders = "+modelLoaders);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())){Log.d(TAG,"startNext: "+modelLoader.getClass());started=true;loadData.fetcher.loadData(helper.getPriority(),this);}}returnstarted;}
@OverridepublicvoidonDataReady(Objectdata){Log.d(TAG,"onDataReady: ");DiskCacheStrategydiskCacheStrategy=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);}}
@Overridepublicvoidreschedule(DecodeJob<?>job){Log.d(TAG,"reschedule: ");// Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself// up.getActiveSourceExecutor().execute(job);}
//DataUrlLoaderprivatestaticfinalStringDATA_SCHEME_IMAGE="data:image";@Overridepublicbooleanhandles(@NonNullModelmodel){// We expect Model to be a Uri or a String, both of which implement toString() efficiently. We// should reconsider this implementation before adding any new Model types.returnmodel.toString().startsWith(DATA_SCHEME_IMAGE);}
//ModelLoaderRegistry@NonNullpublic<A>List<ModelLoader<A,?>>getModelLoaders(@NonNullAmodel){List<ModelLoader<A,?>>modelLoaders=getModelLoadersForClass(getClass(model));if(modelLoaders.isEmpty()){thrownewNoModelLoaderAvailableException(model);}intsize=modelLoaders.size();booleanisEmpty=true;List<ModelLoader<A,?>>filteredLoaders=Collections.emptyList();//noinspection ForLoopReplaceableByForEach to improve perffor(inti=0;i<size;i++){ModelLoader<A,?>loader=modelLoaders.get(i);if(loader.handles(model)){if(isEmpty){filteredLoaders=newArrayList<>(size-i);isEmpty=false;}//根据ModelLoader的handles方法,过滤掉不匹配的//假设传入的是url,则过滤掉DataUrlLoaderfilteredLoaders.add(loader);}}if(filteredLoaders.isEmpty()){thrownewNoModelLoaderAvailableException(model,modelLoaders);}returnfilteredLoaders;}
@NonNullsynchronized<Model>List<ModelLoader<Model,?>>build(@NonNullClass<Model>modelClass){try{List<ModelLoader<Model,?>>loaders=newArrayList<>();for(Entry<?,?>entry:entries){// Avoid stack overflow recursively creating model loaders by only creating loaders in// recursive requests if they haven't been created earlier in the chain. For example:// A Uri loader may translate to another model, which in turn may translate back to a Uri.// The original Uri loader won't be provided to the intermediate model loader, although// other Uri loaders will be.if(alreadyUsedEntries.contains(entry)){continue;}//根据ModelClass进行判断if(entry.handles(modelClass)){alreadyUsedEntries.add(entry);//调用Factory创建Loaderloaders.add(this.<Model,Object>build(entry));alreadyUsedEntries.remove(entry);}}returnloaders;}catch(Throwablet){alreadyUsedEntries.clear();throwt;}}
publicstaticclassStreamFactoryimplementsModelLoaderFactory<String,InputStream>{@NonNull@OverridepublicModelLoader<String,InputStream>build(@NonNullMultiModelLoaderFactorymultiFactory){returnnewStringLoader<>(multiFactory.build(Uri.class,InputStream.class));}@Overridepublicvoidteardown(){// Do nothing.}}
@NonNullpublicsynchronized<Model,Data>ModelLoader<Model,Data>build(@NonNullClass<Model>modelClass,@NonNullClass<Data>dataClass){try{List<ModelLoader<Model,Data>>loaders=newArrayList<>();booleanignoredAnyEntries=false;for(Entry<?,?>entry:entries){// Avoid stack overflow recursively creating model loaders by only creating loaders in// recursive requests if they haven't been created earlier in the chain. For example:// A Uri loader may translate to another model, which in turn may translate back to a Uri.// The original Uri loader won't be provided to the intermediate model loader, although// other Uri loaders will be.if(alreadyUsedEntries.contains(entry)){ignoredAnyEntries=true;continue;}if(entry.handles(modelClass,dataClass)){alreadyUsedEntries.add(entry);loaders.add(this.<Model,Data>build(entry));alreadyUsedEntries.remove(entry);}}//如果存在多个就构建成一个MultiModelLoaderif(loaders.size()>1){returnfactory.build(loaders,throwableListPool);}elseif(loaders.size()==1){returnloaders.get(0);}else{// Avoid crashing if recursion results in no loaders available. The assertion is supposed to// catch completely unhandled types, recursion may mean a subtype isn't handled somewhere// down the stack, which is often ok.if(ignoredAnyEntries){returnemptyModelLoader();}else{thrownewNoModelLoaderAvailableException(modelClass,dataClass);}}}catch(Throwablet){alreadyUsedEntries.clear();throwt;}}