Glide深入分析
一、加载流程源码分析
glide加载图片的基本使用如下:
图片加载基本使用Glide类中重载了多个with()方法,根据Glide所处的场合,我们可以传Activity、Context、Fragment等
重载的With()with()不同的入参不同会影响加载的图片的生命周期,后面会具体分析。with(?)里只有一行代码
with(activity)每个with(?)里都会调用getRetriever(Context).get(?)来获取RequestManager,get(?)对应with()也做了重载,这里我们不深入分析get(?)方法,后续在管理图片生命周期里详细分析。Glide加载图片就依靠这个RequestManager类来管理。接着我们分析RequestManager的asBitmap()方法,除了asBitmap,还有asDrawable()、asGif(),这几个方法主要是告诉Glide加载的二进制图片资源以什么方式返回,
asBitmap()指定加载成Bitmapas(Bitmap.class)会生成一个RequestBuilder对象,显而易见,这里用了builder模式来写的,最终会build一个request出来,然后使用这个request加载图片
as()生成RequestBuilder用于生成request做准备创建出RequestBuilder后马上调用了apply(RequestOptions)设置了一个默认的requestOptions,我们可以修改这个requestOptions来设置占位图、错误图、缓存机制、关闭动画、scaleType等,requestBuilder持有这些设置信息,后续有用。
设置完requestOptions后,调用requestBuilder的load(?)方法来指定加载数据的模式,requestBuilder对load(?)重载了多个,可以让我们可以指定加载来源是String,Uri,drawable等,他们最终都是调用了loadGeneric(@Nullable Object model)
loadGeneric(@Nullable Object model)这里只是简单的记录了加载的model以及标志已经设置了model.
最后调用into(?),这是最重要的一步,多个into(?)的重载方法,最终都是调用into(target, targetListener, getMutableOptions())。本例中我们调用了into(imageView),它会先根据传入的imageView的scaleType来修改requestOptions的参数设置,接着通过glideContext.buildImageViewTarget(imageView,transcodeClass)来生成target对象,这个transcodeClass是我们调用as(Bitmap.class)传进来的,所以本例中的transcodeClass就是Bitmap.clss
最终的into()再来分析下是如何生成target对象的,buildImageViewTarget()里面调用的是ImageViewTargetFactory的buildTarget()
生成target根据clazz的类不同会生成对应的Target本例生成的是BitmapImageViewTarget对象,继承关系如下:
BitmapImageViewTarget<-ImageViewTarget<-ViewTarget,在ViewTarget的构造函数里我们发现创建了一个SizeDeterminer对象,这个类主要的功能是获取ImageView的宽高,SizeDeterminer的最关键的代码是getSize()里的
通过这个SizeDeterminerLayoutListener可以在imageView测量完,在onPreDraw()的时候通知SizeDeterminer可以获取到ImageView的尺寸了,这样就保证ImageView的宽高不为0,宽高加载图片的时候需要。
回到into(),这里组要工作就是获取一个request,然后设置到viewTarget,这样viewTarget就可以控制把控请求了,最后通过requestManager.track()来启动这个request。
如何build出一个request的呢?
buildRequestRecursive()这里主要涉及两个Request,mainRequest和errorRequest,大概的意思是没有errorRequest的时候直接用mainRequest,否则将这两个request包在ErrorRequestCoordinator中,ErrorRequestCoordinator也是一个Request的子类,起到代理的作用。继续往下看mainRequest是如何生成的,buildThumbnailRequestRecursive()处理的事情与buildRequestRecursive()类似,不过它处理的是缩略图,而buildRequestRecursive()处理的是错误的情况,我们发现最后回来到obtainRequest()
关键就在SingleRequest.obtain(),将一系列的参数传进去,先从POOL复用池里获取request,如果没有才去生成SingleRequest对象,所以request是有复用的减少内存开销。request生成了,我们现在回到into(),
into()里后几行将生成的request传给target,通过requestManager.track(target, request)启动request。
那么requestManager是如何处理request的呢?接着往下看
requestManager.track()TargetTracker记录下target,真正启动request就由requestTracker来负责了,
requestTracker.runRequest()所以加载图片的重点就是SingleRequest的begin()方法,篇幅有限代码就不贴了,begin()里限判断model是否为空,也就是你有没有调用RequestBuilder的load()方法,必须先调用load(),再判断request的状态,RUNNING(运行中),COMPLETE(之前就执行完了,直接将数据返回),WAITING_FOR_SIZE(获取尺寸,如果overrideWidth,overrideHeight有效的直接使用onSizeReady(overrideWidth, overrideHeight),否则需要调用viewTarget.getSize(this)来获取尺寸,这时就用到了前面提到的SizeDeterminer,它回调到SingleRequest的onSizeReady(int width, int height),所以最终还是去到了onSizeReady())。当获取到了尺寸才能加载图片,这好理解,所以重任就交给onSizeReady()了,在onSizeReady()中通过engine.load()加载图片
Engine真正去加载数据
onSizeReady()中的代码
入参this是回调,load()会回调到onResourceReady(Resource resource, DataSource dataSource),往下看可以看到target.onResourceReady(result, animation);这样就将result传给了viewTarget,本例中的target是BitmapImageViewTarget,它重写了onResourceReady()方法,在这里将result设置给imageView.
到底如何从网络上获取图片资源,这个就得靠Engine类了。Engine.load()方法里先从缓存中获取资源,如果找到了就回调回去,loadFromActiveResources(),loadFromCache都可以看成是从缓存中取资源,当找不到的时候,接着就通过EngineJob、DecodeJob来处理,核心就是这两个类了。
decodeJob实现了Runnable接口,当调用engine.start(decodeJob),就是通过线程池来执行decodeJob,
engine.start(decodeJob)执行decodeJob的run()方法,实际会去到runWrapped(),
根据runReason获取状态stage,然后再根据stage来获取DataFetcherGenerator,最后执行Gennerator。runReason的初始值是INITIALIZE,getNextStage(Stage.INITIALIZE)
其中diskCacheStrategy是requestOptions中传进来的,默认值是AUTOMATIC,它的decodeCachedResource()和decodeCachedData()都返回true,所以第一次getNextStage(stage)返回的是Stage.RESOURCE_CACHE,回到getNextGenerator()
根据stage首先会得到ResourceCacheGenerator,这时currentGenerator就是ResourceCacheGenerator,执行runGenerators(),
总的来说,DecodeJob的run方法,会依次从ResourceCacheGenerator->DataCacheGenerator->SourceGenerator这样一个链执行,只要其中一个的startNext方法返回为true,则不再寻找下一个Generator。
ResourceCacheGenerator的startNext()先来分析ResourceCacheGenerator得startNext()方法中做了啥?,它先获取了sourceIds,那这个sourceIds到底是什么?
这个方法主要是拿到loadData列表,然后将每个loadData里的sourceKey和alternateKeys存在cacheKeys中返回,LoadData类如下:
LoadData这个类只是简单的持有了key和DataFetcher.
getLoadData()是如何获取到LoadData数据的呢?
获取loadDataGlide注册了很多ModelLoader在Registry里,注册在Glide构造函数里,如Bitmap的ModelLoader
Glide的构造方法的一行显然这里用到了工厂模式,那么工厂生产的产品是什么呢?工厂就是为了生成所需要的ModelLoader,不同的工厂生产各自的ModelLoader。
registry的append(modelclass,dataclass,factory)数据都存在modelLoaderRegistry中,里面是以Entry对象来保存这三个数据的
Entry持有注册的参数知道怎么注册后,我们来看看如何通过Regitry来获取ModelLoader的。
registry的getmodelloaders()modelLoaderRegistry中存有Glide注册的数据,再通过它来获取ModelLoader.那么modelLoaderRegistry.getModelLoaders(model)中到底做了什么?它就是拿到对应model对应的factory调用factory的ModelLoaderbuild(@NonNull MultiModelLoaderFactory multiFactory)(工厂模式)来获取ModelLoader。分析下modelClass是String.class的情况,在Glide的构造方法里就有注册几个如下:
注册的model为String的modelLoader一共有四个model都是String.class,最开始的调用例子中我们调用的是load("xxx"),所以modelClass就是String.class。modelLoaderRegistry.getModelLoaders(model)
modelLoaderRegistry.getModelLoaders(model)又通过getModelLoadersForClass(getClass(model))来获取
getModelLoadersForClass(getClass(model))最后它实际上是调用了MultiModelLoaderFactory的build(entry)
MultiModelLoaderFactory的build(entry)前面提到注册ModelLoader的那三个参数是保存在Entry里的,通过modelClass找到对应的这个entry,因为这里现在举例是String.class所以entry可能会是前面提到的那四个。这里就分析下StringLoader,这个loader里面有三个工厂代表生成不同的loader,
三个工厂entry.factory.build(this)就是调用这是三个工厂的build()方法,
StreamFactory的build()build()中实例了StringLoader,入参是同样是个ModelLoader,入参通过multiFactory.build()来获取
MultiModelLoaderFactory.build(Uri.class, InputStream.class)entry.handles(modelClass, dataClass)主要是判断entry中的modelClass与dataClass是否一致,这里传入的是Uri.class和InputStream.class,所以要找的是符合这个条件的entry,再通过entry的factory build出Uri.class相应的ModelLoader,循环完成后loaders保存着Uri.class对应的所有ModelLoader,从Glide注册ModelLoader的地方可以发现Uri.class对应的ModelLoader有多个,所以loaders.size()>1成立,接着会将loaders包一层,
MultiModelLoaderFactory的Factory的build()将loaders装到了MultiModelLoader类里,得到的是MultiModelLoader,所以StringLoader中持有的uriLoader其实是MultiModelLoader,当调用StringLoader的buildLoadData(),实际是用MultiModelLoader的buildLoaderData(),StringLoader相当代理
MultiModelLoader类的方法buildLoaderData()MultiModelLoader的buildLoadData()操作是遍历modelLoaders找到匹配model的ModelLoader的fetcher存放到fetchers中,然后用fetchers生成MultiFetcher,最后就是生成LoadData。下面看看MultiFetcher的loadData()是如何处理的
MultiFetcher的loadData()它会拿到fetchers中的一个fetcher然后加载数据,callback传入了this,所以数据加载完会回调到onDataReady(@Nullable Data data)
数据不为空就回调上层,如果这个fetcher拿不到数据就调用startNextOrFail()
如果当前的fetcher不是最后一个就调用loadData(priority, callback);继续跑下一个fetcher,否则回调失败。
回到SourceGenerator的startNext()中的loadData.fetcher.loadData(helper.getPriority(), this),这个fetcher的loadData()其实就是MultiFetcher.loadData(),MultiFetcher遍历fetchers里的fetcher直到成功或失败为止。MultiFetcher最终会回调到SourceGenerator的onDataReady(@Nullable Data data)
SourceGenerator的OnDataReady()判断是否需要磁盘缓存,如果需要就赋值给dataToCache,然后调用cb.reschedule(),这里会再次触发SourceGenerator的startNext(),因为dataToCache不为空,所以就会调用cacheData(Object dataToCache),存完后sourceCacheGenerator就不是空了,所以调用sourceCacheGenerator.startNext()从缓存中拿数据,.........最后会回调到decodeJob的onDataFetcherReady(),一层层回调将数据传到viewTarget中显示
Engine加载总结
1、engine先从内存中加载数据,有数据就直接返回,否则执行2
2、engine根据传进来的参数生成EngineJob与DecodeJob,DecodeJob实现了Runnable接口,通过engineJob.start(decodeJob)来执行DecodeJob.
3.执行DecodeJob的run(),依次执行ResourceCacheGenerator、DataCacheGenerator、SourceGenerator的startNext()方法,前者拿到数据就不执行后者,ResourceCacheGenerator、DataCacheGenerator是从磁盘缓存中拿数据,SourceGenerator是没有磁盘缓存的情况执行,如第一次网络、本地加载数据。三个Generator都需要拿到指定model的的ModelLoader,model与ModelLoader是一对多的关系,所以通过model可以拿到多个ModelLoader,多个ModelLoader创建一个MultiModelLoader,当调用MultiModelLoader的buildLoadData()的时候会来成LoadData,这个LoadData入参fetcher是将每个ModelLoader的fetcher提取处理来,然后用所有的fetchers构造出MultiFetcher传给LoadData,加载数据就是通过这个MultiFetcher来完成,用LoadData.fetcher.loadData()来加载数据,一般都是用MultiFetcher来加载数据。MultiFetcher会依次执行每个fetcher,直到没有fetcher或成功或失败为止,获取数据成功后就会回调到DecodeJob的onDataFetcherReady(),最终将数据传到targetView中的ImageView
二、缓存策略
Glide的缓存有内存缓存和磁盘缓存两种。当调用Engine.load()的时候会调用loadFromActiveResources()和loadFromCache来加载内存缓存,如果命中就直接将数据返回,内存缓存可以大大提高加载速度,体验提升。首次加载一张图片,内存中肯定不命中,接着就会搜索磁盘缓存中是否有需要的图片,都找不到的话就需要从文件、远程、asset加载。内存与磁盘都不命中,glide就会执行SourceGenerator获取数据,当获取数据成功回到SourceGenerator的onDataReady(),在这里会判断是否支持磁盘缓存如果支持的话就会触发cacheData(Object dataToCache)来缓存,否则直接返回数据,注意,如果开启了磁盘缓存,SourceGenerator并不需要讲数据结构返回给上层,而是通过sourceCacheGenerator.startNext()从磁盘中读出数据来返回上层(即DecodeJob),decodeJob就将数据一层一层的回调回到TargetView显示
三、如何回收图片资源
当我们启动activity或是fragment的时候,Glide加载了很多图片,当activity或fragment销毁后,并不需要我们手动调用什么,那么到底Glide里怎样知道什么时候回收的呢?这就需要Glide感知activity或fragment的生命周期了。
Glide.with(?)的入参我们可以传activity、fragment、FragmentActivity等,下面我们来分析下Glide.with(activity)
Glide.with(activity)先判断是否是后台线程,如果是后台线程,会调用getApplicationManager(context)来获取RequestManager,因为applicationContext是全局的所以applicationManager加载的图片是伴随app的生命周期
如果不是后台线程,
这里关键是
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
getRequestManagerFragment()用FragmentManager添加了一个RequestManagerFragment,这样可以通过这个fragment的生命周期来知道Activity的生命周期了。RequestManagerFragment通过ActivityFragmentLifecycle来感知生命周期,ActivityFragmentLifecycle中可以添加LifecycleListener来感知生命周期
添加生命感知监听查找这个方法的调用地方,发现RequestManager实现了LifecycleListener接口,且调用的addListener(this),这样当RequestManagerFragment的不同生命周期会触发ActivityFragmentLifecycle对应的方法然后通知每个LifecycleListener,这样就实现了通知RequestManager了,在RequestManager中做相应的回收处理
RequestManager的LifecycleListener接口实现回收图片资源总结:要做到图片回收的关键是需要知道回收的触发时机,换言之就是要感知组件的生命周期,当知道了这些就可以在不同的时刻做相应的处理。Glide巧妙的通过Fragment来间接的捕获Activity或Fragment的生命周期,它通过填充一个空的Fragment来实现感知组件的生命周期,在特定的时刻做相应的处理。