Glide-源码分析(一)
前言
前面几片文章主要介绍了下Picasso
,相对来说Picasso
源码看起来会比较轻松,所以如果想研究图片框架的话,建议先从Picasso
下手,这样会比较容易。
源码分析
今天只分析最简单的一行代码,后面会慢慢深入。 虽然只有一行代码,但是里面的整个逻辑确实非常复杂。
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
复制代码
对,这应该也是我们使用Glide
时候最常用的一句代码,下面我们就一步步跟入。
- with
//------Glide.java------//with重载方法有很多,这里先讲一个public static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}private static RequestManagerRetriever getRetriever(@Nullable Context context) {return Glide.get(context).getRequestManagerRetriever();}
复制代码
第一次触发Glide.get
方法,默认创建一个Glide
对象,如下
public static Glide get(@NonNull Context context) {if (glide == null) {synchronized (Glide.class) {if (glide == null) {checkAndInitializeGlide(context);}}}return glide;}private static void checkAndInitializeGlide(@NonNull Context context) {isInitializing = true;initializeGlide(context);isInitializing = false;}private static void initializeGlide(@NonNull Context context) {initializeGlide(context, new GlideBuilder());}//一步步往下看,就到了这里,真正开始创建Glide对象的方法
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {....//这里代码很多,都省略,直接看最关键的一个方法,buildGlide glide = builder.build(applicationContext);....Glide.glide = glide;}
复制代码
以上是查找的过程的代码,不是特别重要,下面是GlideBuilder的build方法,非常重要。
Glide build(@NonNull Context context) {if (sourceExecutor == null) {sourceExecutor = GlideExecutor.newSourceExecutor();}if (diskCacheExecutor == null) {diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();}if (animationExecutor == null) {animationExecutor = GlideExecutor.newAnimationExecutor();}if (memorySizeCalculator == null) {memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();}if (connectivityMonitorFactory == null) {connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();}if (bitmapPool == null) {int size = memorySizeCalculator.getBitmapPoolSize();if (size > 0) {bitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}}if (arrayPool == null) {arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());}if (memoryCache == null) {memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());}if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);}if (engine == null) {engine =new Engine(memoryCache,diskCacheFactory,diskCacheExecutor,sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor(),GlideExecutor.newAnimationExecutor(),isActiveResourceRetentionAllowed);}RequestManagerRetriever requestManagerRetriever =new RequestManagerRetriever(requestManagerFactory);return new Glide(context,engine,memoryCache,bitmapPool,arrayPool,requestManagerRetriever,connectivityMonitorFactory,logLevel,defaultRequestOptions.lock(),defaultTransitionOptions);}
复制代码
Glide对象里面存的东西很多,上面初始化了很多重要的东西,先不深入去看,但是根据名字也能大概的猜到对象的作用。
- sourceExecutor 获取源数据线程池
- diskCacheExecutor 获取diskcache线程池
- animationExecutor 应该是跟动画有关的线程池
- memorySizeCalculator 应该是个工具类,来计算内存的大小
- connectivityMonitorFactory 应该是连接监听的一个工厂类
- bitmapPool 存放bitmap的池
- arrayPool 存放数组的池
- memoryCache 资源缓存对象
- diskCacheFactory disk cache工厂类
- requestManagerRetriever requestManager训练器
大概就这样一个初步的印象,可能不正确,后面可以慢慢验证。 下面继续回到刚才的with的方法。
@NonNullprivate static RequestManagerRetriever getRetriever(@Nullable Context context) {return Glide.get(context).getRequestManagerRetriever();}
复制代码
这里的getRequestManagerRetriever,其实就是build方法中直接new出来的RequestManagerRetriever
public static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}
复制代码
所以我们再跟进去看下RequestManagerRetriever
的get
方法
@NonNullpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}
...@NonNullprivate RequestManager supportFragmentGet(@NonNull Context context,@NonNull FragmentManager fm,@Nullable Fragment parentHint,boolean isParentVisible) {//创建一个SupportRequestManagerFragment,来获取生命周期的状态,来对图片进行管理(这个后面再深入,这里可以简单理解为,就是为了利用Fragment的生命周期)SupportRequestManagerFragment current =getSupportRequestManagerFragment(fm, parentHint, isParentVisible);RequestManager requestManager = current.getRequestManager();//刚开始创建的SupportRequestManagerFragment的requestManager==nullif (requestManager == null) {Glide glide = Glide.get(context);requestManager =factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);current.setRequestManager(requestManager);}return requestManager;}
复制代码
get
方法有很多重载,这里我们就以参数为FragmentActivity
为例子。 get
方法主要是为了创建RequestManager
.
回过头再看Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
, 接下来就是调用了RequestManager
对象的load
方法
public RequestBuilder<Drawable> load(@Nullable String string) {return asDrawable().load(string);}
...
@NonNull@CheckResultpublic RequestBuilder<Drawable> asDrawable() {return as(Drawable.class);}
...//调用了as方法,其实主要就是创建一个RequestBuilder对象,然后传入最终要转换成的资源类型,显然默认是转换为Drawable.class@NonNull@CheckResultpublic <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {return new RequestBuilder<>(glide, this, resourceClass, context);}
...@NonNull@Override@CheckResultpublic RequestBuilder<TranscodeType> load(@Nullable String string) {return loadGeneric(string);}
...//调用load方法传入url地址时,并没有真正的去发生请求获取到图片,只是设置了一个参数
@NonNullprivate RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {this.model = model;isModelSet = true;return this;}
复制代码
这里的代码其实比较简单,没有什么好介绍的,简单的说就是使用了建造者模式,然后创建了一个新的对象RequestBuilder
,然后传入一些参数。既然是建造者模式,那么最后RequestBuilder
肯定会生成一个Request
。
接下来再回头看
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
复制代码
下面就应该是调用RequestBuilder
的into
方法了
@NonNullpublic ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {...return into(glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions);}
...
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,@NonNull RequestOptions options) {...Request request = buildRequest(target, targetListener, options);//获取到当前target绑定的request请求,如果现在正在运行的这个请求跟这个target之前绑定的请求是一样的话,//就判断下之前的请求是否有再运行,没有运行就开始运行,有运行就不操作。并且回收当前要运行的Request对象Request previous = target.getRequest();if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {request.recycle();...if (!Preconditions.checkNotNull(previous).isRunning()) {...previous.begin();}return target;}requestManager.clear(target);//让target跟request绑定target.setRequest(request);//这里才是正在发起请求的地方requestManager.track(target, request);return target;}
复制代码
into方法中有2句比较关键的地方,这里提取出来单独来讲。
1. Request request = buildRequest(target, targetListener, options);
2. requestManager.track(target, request);
复制代码
先看1,在RequestBuilder
中调用buildRequest,构建一个Request
对象
private Request buildRequest(Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,RequestOptions requestOptions) {return buildRequestRecursive(target,targetListener,/*parentCoordinator=*/ null,transitionOptions,requestOptions.getPriority(),requestOptions.getOverrideWidth(),requestOptions.getOverrideHeight(),requestOptions);
...
private Request buildRequestRecursive(Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,RequestOptions requestOptions) {...Request mainRequest =buildThumbnailRequestRecursive(target,targetListener,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight,requestOptions);...return errorRequestCoordinator;}
...//顾名思义,创建一个缩略图的Request,先判断是否有设置缩放的一些熟悉
//如果没有,就获取一个没有缩放的Request
private Request buildThumbnailRequestRecursive(Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,RequestOptions requestOptions) {if (thumbnailBuilder != null) {...} else if (thumbSizeMultiplier != null) {...Request thumbnailRequest =obtainRequest(target,targetListener,thumbnailOptions,coordinator,transitionOptions,getThumbnailPriority(priority),overrideWidth,overrideHeight);coordinator.setRequests(fullRequest, thumbnailRequest);return coordinator;} else {return obtainRequest(target,targetListener,requestOptions,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight);}}//绕了半天,这里才是真正创建一个Request的地方
private Request obtainRequest(Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,RequestOptions requestOptions,RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight) {return SingleRequest.obtain(context,glideContext,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,glideContext.getEngine(),transitionOptions.getTransitionFactory());}
复制代码
看一些优秀的三方源码,总是这样,方法重载很多,方法参数很多,很容易晕,大家要一步步往里面跟,总能看懂的。
这里我们最后找到的是创建了一个SingleRequest
对象。当然如果说你在 Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
设置了一些宽高,或者是缩放的属性,那么走的分支可能就不是这个。后面我们再分析。先从最简单的分支入手,一步步解析。
接下来我们继续看第2句关键的代码
requestManager.track(target, request);
复制代码
void track(@NonNull Target<?> target, @NonNull Request request) {...requestTracker.runRequest(request);}public void runRequest(@NonNull Request request) {requests.add(request);if (!isPaused) {//正常情况isPaused=false,走这个分支,开始请求request.begin();} else {request.clear();if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Paused, delaying request");}pendingRequests.add(request);}}
...public void begin() {...status = Status.WAITING_FOR_SIZE;//这里先判断overrideWidth,overrideHeight是否合法if (Util.isValidDimensions(overrideWidth, overrideHeight)) {onSizeReady(overrideWidth, overrideHeight);} else {target.getSize(this);}if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)&& canNotifyStatusChanged()) {target.onLoadStarted(getPlaceholderDrawable());}...}复制代码
我们这里直接看到begin方法
private static boolean isValidDimension(int dimen) {return dimen > 0 || dimen == Target.SIZE_ORIGINAL;}
复制代码
由于我们并没有设置宽高,所以返回false,走下面分支
target.getSize(this);public void getSize(@NonNull SizeReadyCallback cb) {sizeDeterminer.getSize(cb);}void getSize(@NonNull SizeReadyCallback cb) {int currentWidth = getTargetWidth();int currentHeight = getTargetHeight();if (isViewStateAndSizeValid(currentWidth, currentHeight)) {cb.onSizeReady(currentWidth, currentHeight);return;}if (!cbs.contains(cb)) {cbs.add(cb);}if (layoutListener == null) {ViewTreeObserver observer = view.getViewTreeObserver();layoutListener = new SizeDeterminerLayoutListener(this);observer.addOnPreDrawListener(layoutListener);}}
复制代码
这里比较关键的就是target.getSize(this);
方法中的参数this
,这里的this
是一个SizeReadyCallback
. 而SingleRequest
实现了SizeReadyCallback
这里就是等待layout布局后,ImageView
有了width
和height后
就会进入SingleRequest
的onSizeReady
回调方法。
主要的通过
ViewTreeObserver observer = view.getViewTreeObserver();layoutListener = new SizeDeterminerLayoutListener(this);observer.addOnPreDrawListener(layoutListener);
复制代码
这3句来监听ImageView
的布局之后的一个回调,也就是有了width
和height
之后的回调。
如果我们本身设置了缩放,或者是宽高属性,那么Glide就会直接使用width
和height
当作参数,调用 onSizeReady
.下面我们直接看。
public void onSizeReady(int width, int height) {...if (status != Status.WAITING_FOR_SIZE) {return;}status = Status.RUNNING;float sizeMultiplier = requestOptions.getSizeMultiplier();this.width = maybeApplySizeMultiplier(width, sizeMultiplier);this.height = maybeApplySizeMultiplier(height, sizeMultiplier);...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);...if (status != Status.RUNNING) {loadStatus = null;}...}
复制代码
这里比较关键的地方
- 状态的切换
- 调用engine.load
第1点就不说了,直接看上面代码就好。 直接看第2点engine.load
我们首先看下engine
这个对象是在哪里初始化的。
private Request obtainRequest(Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,RequestOptions requestOptions,RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight) {return SingleRequest.obtain(context,glideContext,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,glideContext.getEngine(),transitionOptions.getTransitionFactory());}
复制代码
可以看出来是前面构建SingleRequest
对象的时候glideContext.getEngine()
传入的一个参数。
glideContext =new GlideContext(context,arrayPool,registry,imageViewTargetFactory,defaultRequestOptions,defaultTransitionOptions,engine,logLevel);
复制代码
而glideContext中的engine
也是参数传入的。 最终找到
@NonNullGlide build(@NonNull Context context) {...if (engine == null) {engine =new Engine(memoryCache,diskCacheFactory,diskCacheExecutor,sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor(),GlideExecutor.newAnimationExecutor(),isActiveResourceRetentionAllowed);}...return new Glide(context,engine,memoryCache,bitmapPool,arrayPool,requestManagerRetriever,connectivityMonitorFactory,logLevel,defaultRequestOptions.lock(),defaultTransitionOptions);}
复制代码
是在创建Glide的时候new出来的一个Engine
,不自己传入的话,会默认构建一个。然后供后面使用。 下面我们继续看Engine.load
方法
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) {...EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;}EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;}EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb);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);engineJob.start(decodeJob);...return new LoadStatus(cb, engineJob);}
复制代码
代码很多,我们就看重点几个地方。 先通过参数,生成key,通过key,去获取缓存数据,如果有缓存就直接调用SingleRequest
的onResourceReady
方法。
//生成key,其实可以理解为就是一个字符串,然后key-value,获取到对应的缓存EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);
//然后调用
loadFromActiveResources
//然后调用
loadFromCache复制代码
如果缓存中都没有数据,那么就继续下面
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}复制代码
先从jobs里面通过key获取前面已经加入的EngineJob。如果有,就直接current.addCallback(cb);
。 意思就是说,前面如果已经执行过一个任务了,就会把任务添加到jobs
,如果后面遇到相同的任务了,就直接在jobs里面获取,可以把jobs就认为是一个HashMap,根据key
来保存。
如果说,是第一次运行任务,也就是加载图片,那么current==null,继续往下走。
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);engineJob.start(decodeJob);...return new LoadStatus(cb, engineJob);
复制代码
创建2个对象,EngineJob和DecodeJob。 jobs.put(key, engineJob);
这里是为了后面如果是加载相同的图片的话,这里会直接获取到EngineJob
然后去处理,而不是每次都新建一个。
接下来继续engineJob.start(decodeJob);
public void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;GlideExecutor executor = decodeJob.willDecodeFromCache()? diskCacheExecutor: getActiveSourceExecutor();executor.execute(decodeJob);}
...
boolean willDecodeFromCache() {Stage firstStage = getNextStage(Stage.INITIALIZE);return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;}
复制代码
这里判断了下是使用diskCacheExecutor
还是getActiveSourceExecutor()
, 其实第一次看源码的时候,我们可以先来走一遍完整的流程,这2者具体的区别我们先不要太在意。
这2者其实都是一个ExecutorService
,是用来处理线程的。
那我们继续。
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable
复制代码
DecodeJob实现了Runnable接口 这里调用
executor.execute(decodeJob);
复制代码
其实就是在线程池中找一个线程来执行decodeJob中的run方法。
@Overridepublic void run() {...DataFetcher<?> localFetcher = currentFetcher;try {if (isCancelled) {notifyFailed();return;}runWrapped();} catch (Throwable t) {...}
}//通过状态来获取不同的生成器,来生成资源
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);}}
...
//获取当前状态的下一个状态
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);}}
复制代码
上面最重要的方法就是runWrappeed
。 主要3点。
- 获取当前状态的下一个状态,然后赋值给当前状态
- 获取与当前匹配的Generator
- 运行生成器来获取资源。
我们先来看第一点,也就是getNextStage
方法,通过当前状态,来获取后面的一个状态。其实很简单。 状态的顺序就是
INITIALIZE(初始化)-> RESOURCE_CACHE(获取内存缓存)-> DATA_CACHE(获取磁盘缓存)-> SOURCE(真正去请求资源)-> FINISHED(完成)
复制代码
正常情况下,就会按这样步骤一个个来,但是有些时候我们会设置不缓存的一些参数,那么就会跳过某个步骤。
在代码中也有体现
case INITIALIZE:return diskCacheStrategy.decodeCachedResource()? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
复制代码
初始化后应该是从获取资源缓存,但是diskCacheStrategy.decodeCachedResource()
返回false
的话,那么就直接获取getNextStage(Stage.RESOURCE_CACHE)
,也就是资源缓存的下一个状态。
DiskCacheStrategy
代表缓存的策略,一共有
- DiskCacheStrategy.ALL
- DiskCacheStrategy.NONE
- DiskCacheStrategy.DATA
- DiskCacheStrategy.RESOURCE
- DiskCacheStrategy.AUTOMATIC
我们这里来看下DiskCacheStrategy.NONE
public static final DiskCacheStrategy NONE = new DiskCacheStrategy() {public boolean isDataCacheable(DataSource dataSource) {return false;}public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {return false;}public boolean decodeCachedResource() {return false;}public boolean decodeCachedData() {return false;}};
复制代码
这里就都返回false。不允许获取缓存数据。
下面来看第2点,获取Generator
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);}}
复制代码
通过不同的stage获取到不同的Generator
。一开始获取到ResourceCacheGenerator
。
下面看第3点,runGenerators
private void runGenerators() {...while (!isCancelled && currentGenerator != null&& !(isStarted = currentGenerator.startNext())) {stage = getNextStage(stage);currentGenerator = getNextGenerator();if (stage == Stage.SOURCE) {reschedule();return;}}}
复制代码
这里代码很简单,取消的情况先不考虑,主要是这句代码
!(isStarted = currentGenerator.startNext())
复制代码
Generator
中注册了很多ModelLoader<File, ?>
,ModelLoader<File, ?>
可以生成对应的处理资源的LoadData
, 不同的LoadData
只能加载自己能加载到的资源。
public boolean startNext() {...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;}
复制代码
最终调用的是LoadData
的fetcher
的loadData方法。 这里的fetcher
是HttpUrlFetcher
(为什么是这个后面可以再深入讲,先走完整个流程)。
public void loadData(@NonNull Priority priority,@NonNull DataCallback<? super InputStream> callback) {...InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());callback.onDataReady(result);...}
复制代码
loadDataWithRedirects
这个方法就不深入介绍了,使用了HttpURLConnection
去请求资源。 请求完成后,调用了onDataReady
方法,把结果往上传。
这里我们就要一步步往上找到回调方法。
首先刚才在SourceGenerator
调用的
loadData.fetcher.loadData(helper.getPriority(), this);
复制代码
会发现,参数 DataCallback就是SourceGenerator
。
所以回调的其实是SourceGenerator
的onDataReady
方法
@Overridepublic void onDataReady(Object data) {...dataToCache = data;cb.reschedule();...}
复制代码
这里又有一个cb也就是Callback
,继续往前找,发现是DecodeJob
, 所以这里又调用了,DecodeJob.reschedule();
public void reschedule() {runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;callback.reschedule(this);}
复制代码
DecodeJob
中又调用了callback.reschedule
,其实这里的callback
是EngineJob
。很多优秀的三方库就是这样,绕来绕去的,看起来比较费劲。
@Overridepublic void reschedule(DecodeJob<?> job) {getActiveSourceExecutor().execute(job);}
复制代码
这里会发现一个很奇怪的东西,因为在前面我们已经介绍过了,入口就是执行DecodeJob
的run
方法,然后执行完成之后一步步回调,这里竟然又去执行DecodeJob
的run
,死循环么,当然不是,我们继续往下看。
之后的流程跟前面都一样,这里就不再赘述了,然后又到了SourceGenerator
的startNext
方法
public boolean startNext() {if (dataToCache != null) {Object data = dataToCache;dataToCache = null;cacheData(data);}if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {return true;}..return started;}
...
private void cacheData(Object dataToCache) {...sourceCacheGenerator =new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);}
复制代码
这个时候跟之前就不一样了,因为数据请求已经回来了,dataToCache!=null
,然后调用cacheData
方法,把数据缓存起来。 调用cacheData
方法之后,最后创建了一个DataCacheGenerator
。然后调用startNext
方法。
public boolean startNext() {while (modelLoaders == null || !hasNextModelLoader()) {sourceIdIndex++;if (sourceIdIndex >= cacheKeys.size()) {return false;}Key sourceId = cacheKeys.get(sourceIdIndex);@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;}
复制代码
这里的代码呢,其实跟前面的SourceGenrator
差不多,由于前面已经缓存了数据,所以cacheFile!=null,获取到的modelLoader
其实是ByteBufferFileLoader
,然后fetcher
是ByteBufferFetcher
,所以
public void loadData(@NonNull Priority priority,@NonNull DataCallback<? super ByteBuffer> callback) {ByteBuffer result;try {result = ByteBufferUtil.fromFile(file);} catch (IOException e) {...callback.onLoadFailed(e);return;}callback.onDataReady(result);}
复制代码
直接加载文件,返回二进制数组,然后调用回调函数。这里的callback
其实就是DataCacheGenerator
,跟前面一样的,就是不停往前面找。仔细点,还是很简单的。
@Overridepublic void onDataReady(Object data) {cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);}
复制代码
这里cb是DecodeJob
。
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,DataSource dataSource, Key attemptedKey) {...runReason = RunReason.DECODE_DATA;callback.reschedule(this);...
复制代码
又开始了,设置了一下值runReason = RunReason.DECODE_DATA,又调用了callback
也就是EngineJob
的reschedule
方法。
这里我就不继续往前找了,最后还是调用了DecodeJob
的run
方法
public void run() {...runWrapped();...}
private void runWrapped() {switch (runReason) {...case DECODE_DATA:decodeFromRetrievedData();break;...}}
private void decodeFromRetrievedData() {...Resource<R> resource = null;try {resource = decodeFromData(currentFetcher, currentData, currentDataSource);...if (resource != null) {notifyEncodeAndRelease(resource, currentDataSource);} else {runGenerators();}}private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {....notifyComplete(result, dataSource);
...}private void notifyComplete(Resource<R> resource, DataSource dataSource) {...callback.onResourceReady(resource, dataSource);}
复制代码
这里就比较简单了,获取到资源然后解码后,调用callback也就是EngineJob
的onResourceReady
方法
public void onResourceReady(Resource<R> resource, DataSource dataSource) {...MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();}public boolean handleMessage(Message message) {...switch (message.what) {case MSG_COMPLETE:job.handleResultOnMainThread();...}void handleResultOnMainThread() {...cb.onResourceReady(engineResource, dataSource);...}
复制代码
这里的cb其实就是SingleRequest
了。
public void onResourceReady(Resource<?> resource, DataSource dataSource) {...onResourceReady((Resource<R>) resource, (R) received, dataSource);}private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {...target.onResourceReady(result, animation);...}
复制代码
这里的target
其实就是ImageViewTarget
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {...setResourceInternal(resource);...}
private void setResourceInternal(@Nullable Z resource) {...setResource(resource);...}protected abstract void setResource(@Nullable Z resource);
复制代码
可以看出setResource
是一个抽象方法。 由于之前传入的是Drawable.class
所以这里的实现是DrawableImageViewTarget
protected void setResource(@Nullable Drawable resource) {view.setImageDrawable(resource);}
复制代码
这里的view其实就是我们的ImageView
到这里,最简单的Glide加载网络图片的流程已经走完。
如果对整个流程不懂的同学,其实是可以debug一下,然后一步一步跟进去。但是由于流程跳来跳去的,可能断点不是很好打。 大家可以先看下我的整个流程,了解大概之后就可以自己打断点了。
后续还会继续深入Glide
源码。有兴趣的同学可以关注下。
Glide-源码分析(一)相关推荐
- Android 图片框架原理——Glide源码分析
目录 前言 一.With() 二.load() 三.into() 1. buildImageViewTarget() 2.关注1 :buildRequest 构建 3.关注2:runRequest 执 ...
- 图片加载—Glide为什么这么强?Glide源码分析(下)
写在前面 Github–Glide 镇楼 欢迎光临下篇 ~ 目录 (上) 1.代码实例 2.GlideApp 3.with 4.监听生命周期 5.load 6.into (下) 1.资源加载和缓存机制 ...
- Glide 源码分析与面试提问
问题: 1.使用的什么网络请求 使用的HttpURLConnection com.bumptech.glide.load.model.stream.HttpGlideUrlLoader com.bum ...
- Glide源码分析3 -- 绑定Activity生命周期
1. 概述和核心类 Glide中一个重要特性是Request可以随Activity或Fragment的onStart而resume,onStop而pause,onDestroy而clear,从而节约流 ...
- Glide源码阅读理解一小时
前言 这篇图.文.表.代码一起组成的 Glide 源码分析的文章是在上一篇文章 Android-Universal-Image-Loader源码分析 中之后的又一篇图片加载框架源码解析,它也具备了 I ...
- 从源码分析Android的Glide库的图片加载流程及特点
转载:http://m.aspku.com/view-141093.html 这篇文章主要介绍了从源码分析Android的Glide库的图片加载流程及特点,Glide库是Android下一款人气很高的 ...
- Glide源码解析-加载流程
1 引言 一直想要阅读Glide源码,但是苦于时间和功力都不够,总是断断续续的,趁着现在有一些空暇时间,来简要分析Glide的源码.Glide的实现太过复杂,不可能做到面面俱到,如果每一行都细致分析, ...
- Lifecycle 使用与源码分析——彻底搞懂Lifecycle原理
一.Lifecycle 介绍 Lifecycle是一个生命周期感知组件,一般用来响应Activity.Fragment等组件的生命周期变化,并将变化通知到已注册的观察者.有助于更好地组织代码,让代码逻 ...
- Lifecycle 使用与源码分析
一.Lifecycle 介绍 Lifecycle是一个生命周期感知组件,一般用来响应Activity.Fragment等组件的生命周期变化,并将变化通知到已注册的观察者.有助于更好地组织代码,让代码逻 ...
- 【Android CameraX】CameraXBasic —— 官方CameraX实例源码分析
一.简介 二.源码分析 2.1 build.gradle 2.2 代码结构 2.3 变量 2.3.1 lensFacing 2.3.2 preview 2.3.3 Image capture 2.3. ...
最新文章
- Objective-C设计模式——外观Faced(接口适配)
- 模式分类笔记--聚类分析算法
- 张平文当选美国工业与应用数学学会会士
- bzoj3231 [SDOI2008]递归数列 矩乘
- 哪一类人用苹果手机最多?
- mysql workbench uml_Ubuntu 16.04下UML建模PowerDesigner的替代ERMaster和MySQL Workbench
- 【备注】【C11】《Android游戏编程之从零开始》PDF 下载
- Hibernate二级缓存
- 清除dnf垃圾进程 .bat文件
- Golang8小时基础入门
- 笔记本电脑怎么录制视频
- 正则表达式中问号(?)的用法详解
- php添加页脚,WordPress网站页脚footer.php修改图文教程
- c语言编译器前端实现,一个编译器(前端)的实现
- 河南理工大学matlab,有没有会MATLAB的大神
- 超分算法小合集之SRCNN、DCSCN、SRDenseNet、SRGAN
- 量化交易存在哪些缺陷?
- 计算机word文本段落位置互换,切换、调换、互换word段落的位置
- 用java计算电阻,NTC热敏电阻计算公式
- 小米技术委员能扛起雷军技术立业的大旗吗?