本系列文章是在郭霖的文章基础上进行写作的
在使用Glide的时候我们经常就下面的一句语句就完成了图片加载的工作,那么你是否知道里面究竟发生了什么事情?
1 | Glide.with(context).load(URL).into(ImageView); |
with()
Glide开始于with()。传递的参数可以是Context、Activity、Fragment(方法重载),返回的是RequestManager,load()就是从它开始的。
1 | public static RequestManager with(Context context) { |
- 当参数是Context的时候,这时候Glide就可以通过这个参数得应用的生命周期,应用程序关闭的话,Gli停止加载。
- 当参数是Activity/Fragment的时候,会向当前的Activity当中添加一个隐藏的Fragment,因为Fragment的生命周期和Activity同步,如果Activity被销毁的时候,添加的Fragment可以监听到,这样Glide就会捕获这个事件并停止加载事件。
with()其实就是为了得到一个RequestManager对象,然后Glide会根据我们传入with()方法的参数来确定图片加载的生命周期
返回的是RequestManager,用于第二个过程load()的开始。
load()
Glide是支持图片URL字符串、Bitmap、Drawable、Uri 、File、resourceId等加载形式的,因此RequestManager中也有很多个load()方法的重载。
下面以加载URL字符串的load()方法进行研究
1 | public class RequestManager implements LifecycleListener ,ModelTypes<RequestBuilder<Drawable>>{ |
load()方法
- 先调用了asDrawable()方法
- 再调用load()方法
- 然后把传入的图片URL地址传进去。
asDrawable()方法返回的是 RequestBuilder,建立请求
loadGeneric()方法
load()源码
1 | /** |
loadGeneric()方法是要返回一个RequestBuilder对象的,因此在loadGeneric()方法的最后return this
,然后把string –> model对象.
RequestManager源码
1 | /** |
它提供了asBitmap()和asGif()这两个方法,分别是用于强制指定加载静态图片和动态图片。
RequestBuilder
我们再回到RequestManager的load()方法中。我们来看下RequestBuilder
这个类的源码:
1 | public class RequestBuilder<TranscodeType> implements Cloneable, |
为了节省篇幅,只留下了方法名,去除了方法体。RequestBuilder中有很多个方法,这些方法其实就是Glide绝大多数的API了。
你会发现RequestBuilder类中有into()方法,也就是说,最终load()方法返回的其实就是一个DrawableTypeRequest对象???。那么接下来我们就要进行第三步了,分析into()方法中的逻辑。
into()
into()方法的具体逻辑都是在RequestBuilder的父类当中了。
RequestBuilder中的into()方法
1 | /** |
现在我们只需要关注最后一行代码。最后一行代码先是调用了glideContext.buildImageViewTarget()方法,这个方法会构建出一个Target对象。
Target对象则是用来最终展示图片用的,源码如下:
buildImageViewTarget
1 | <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) { |
这里其实又是调用了ImageViewTargetFactory的buildTarget()方法
ImageViewTargetFactory
1 | public class ImageViewTargetFactory { |
在buildTarget()方法中会根据传入的class参数来构建不同的Target对象。
这个class参数其实基本上只有两种情况,如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象
否则的话构建的都是GlideDrawableImageViewTarget对象。至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。
通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。
现在回到刚才into()方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。我们来看一下这个into()方法的源码:
1 | public <Y extends Target<TranscodeType>> Y into(Y target) { |
第15行调用buildRequest()方法构建出了一个Request对象,还有第18行来执行这个Request。
Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
buildRequest
1 | private Request buildRequest( |
buildRequest()方法的内部其实又调用了buildRequestRecursive()方法
buildRequestRecursive()方法中的代码都是在处理缩略图的。
如果我们只追主线流程的话,其实这里都是处理一些像placeholderId、errorPlaceholder、diskCacheStrategy。因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。上面的buildRequestRecursive()又接连的调用了下面的方法
1 | private Request buildThumbnailRequestRecursive( |
最新版本跟丢,还是回到郭霖老师的版本中继续
所以至此已经构建了一个Request对象,接下来我们看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示:
1 | /** |
有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request
否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:
begin
1 |
|
首先如果model等于null,model也就是我们在第二步load()方法中传入的图片URL地址,这个时候会调用onException()方法。
如果你跟到onException()方法里面去看看,你会发现它最终会调用到一个setErrorPlaceholder()当中,如下所示:
setErrorPlaceholder
1 | private void setErrorPlaceholder(Exception e) { |
这个方法中会先去获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用target.onLoadFailed()方法并将占位图传入。
那么onLoadFailed()方法中做了什么呢?我们看一下:
1 | public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter { |
就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。
而如果你仔细看下刚才begin()方法的第15行,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这就是placeholder()和error()这两个占位图API底层的实现原理。
我们继续回到begin()方法。
刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。
这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。
如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。
如果没指定的话,就会执行第12行代码,调用target.getSize()方法。
这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。
具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用onSizeReady()方法。也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,
onSizeReady
1 | @Override |
先来看一下,在第12行调用了loadProvider.getModelLoader()方法,那么我们第一个要搞清楚的就是,这个loadProvider是什么?要搞清楚这点,需要先回到第二步的load()方法当中。
还记得load()方法是返回一个DrawableTypeRequest对象吗?刚才我们只是分析了DrawableTypeRequest当中的asBitmap()和asGif()方法,并没有仔细看它的构造函数,现在我们重新来看一下DrawableTypeRequest类的构造函数:
1 | public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions { |
在第29行,也就是构造函数中,调用了一个buildProvider()方法,并把streamModelLoader和fileDescriptorModelLoader等参数传入到这个方法中,这两个ModelLoader就是之前在loadGeneric()方法中构建出来的。
再来看一下buildProvider()方法里面做了什么,在第16行调用了glide.buildTranscoder()方法来构建一个ResourceTranscoder,它是用于对图片进行转码的,由于ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象。
在第18行调用了glide.buildDataProvider()方法来构建一个DataLoadProvider,它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。
在第20行,new了一个ImageVideoModelLoader的实例,并把之前loadGeneric()方法中构建的两个ModelLoader封装到了ImageVideoModelLoader当中。
在第22行,new出一个FixedLoadProvider,并把刚才构建的出来的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。
我们回到onSizeReady()方法中,在onSizeReady()方法的第12行和第18行,分别调用了loadProvider的getModelLoader()方法和getTranscoder()方法,那么得到的对象也就是刚才我们分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。而在第13行,又调用了ImageVideoModelLoader的getResourceFetcher()方法
1 | public class ImageVideoModelLoader<A> implements ModelLoader<A, ImageVideoWrapper> { |
在第20行会先调用streamLoader.getResourceFetcher()方法获取一个DataFetcher,而这个streamLoader其实就是我们在loadGeneric()方法中构建出的StreamStringLoader,调用它的getResourceFetcher()方法会得到一个HttpUrlFetcher对象。
然后在第28行new出了一个ImageVideoFetcher对象,并把获得的HttpUrlFetcher对象传进去。也就是说,ImageVideoModelLoader的getResourceFetcher()方法得到的是一个ImageVideoFetcher。
我们再次回到onSizeReady()方法,在onSizeReady()方法的第23行,这里将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来我们就要看一看,这个Engine的load()方法
load()
1 | public class Engine implements EngineJobListener, |
只需要从第45行看起就行。这里构建了一个EngineJob,它的主要作用就是用来开启线程的,为后面的异步加载图片做准备。
接下来第46行创建了一个DecodeJob对象,从名字上来看,它好像是用来对图片进行解码的,但实际上它的任务十分繁重,待会我们就知道了。
继续往下看,第48行创建了一个EngineRunnable对象,并且在51行调用了EngineJob的start()方法来运行EngineRunnable对象,这实际上就是让EngineRunnable的run()方法在子线程当中执行了。
那么我们现在就可以去看看EngineRunnable的run()方法,如下所示:
run
1 | @Override |
在第9行,这里调用了一个decode()方法,并且这个方法返回了一个Resource对象。看上去所有的逻辑应该都在这个decode()方法执行的了,那我们跟进去瞧一瞧:
decode
1 | private Resource<?> decode() throws Exception { |
decode()方法中又分了两种情况,从缓存当中去decode图片的话就会执行decodeFromCache(),否则的话就执行decodeFromSource()。本篇文章中我们不讨论缓存的情况,那么就直接来看decodeFromSource()方法的代码,如下所示
1 | private Resource<?> decodeFromSource() throws Exception { |
这里又调用了DecodeJob的decodeFromSource()方法
decodeFromSource
1 | class DecodeJob<A, T, Z> { |
我们先来看一下decodeFromSource()方法,其实它的工作分为两部,第一步是调用decodeSource()方法来获得一个Resource对象,第二步是调用transformEncodeAndTranscode()方法来处理这个Resource对象。
我们先来看第一步,decodeSource()方法中的逻辑也并不复杂,首先在第14行调用了fetcher.loadData()方法。其实就是刚才在onSizeReady()方法中得到的ImageVideoFetcher对象,这里调用它的loadData()方法,代码如下所示:
$
1 | @Override |
在ImageVideoFetcher的loadData()方法的第6行,这里又去调用了streamFetcher.loadData()方法,然就是刚才在组装ImageVideoFetcher对象时传进来的HttpUrlFetcher了。
因此这里又会去调用HttpUrlFetcher的loadData()方法,那么我们继续跟进去瞧一瞧:
1 | public class HttpUrlFetcher implements DataFetcher<InputStream> { |
loadData()方法只是返回了一个InputStream,回到刚才ImageVideoFetcher的loadData()方法中,在这个方法的最后一行,创建了一个ImageVideoWrapper对象,并把刚才得到的InputStream作为参数传了进去。
我们回到再上一层,也就是DecodeJob的decodeSource()方法当中,在得到了这个ImageVideoWrapper对象之后,紧接着又将这个对象传入到了decodeFromSourceData()当中,来去解码这个对象。decodeFromSourceData()方法的代码如下所示
decodeFromSourceData
1 | private Resource<T> decodeFromSourceData(A data) throws IOException { |
在第7行调用了loadProvider.getSourceDecoder().decode()方法来进行解码。
loadProvider就是刚才在onSizeReady()方法中得到的FixedLoadProvider,而getSourceDecoder()得到的则是一个GifBitmapWrapperResourceDecoder对象,也就是要调用这个对象的decode()方法来对图片进行解码。
来看下GifBitmapWrapperResourceDecoder的代码:
GifBitmapWrapperResourceDecoder
1 | public class GifBitmapWrapperResourceDecoder implements ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> { |
首先,在decode()方法中,又去调用了另外一个decode()方法的重载。
然后在第23行调用了decodeStream()方法,准备从服务器返回的流当中读取数据。
decodeStream()方法中会先从流中读取2个字节的数据,来判断这张图是GIF图还是普通的静图,如果是GIF图就调用decodeGifWrapper()方法来进行解码,如果是普通的静图就用调用decodeBitmapWrapper()方法来进行解码。
这里我们只分析普通静图的实现流程.
来看一下decodeBitmapWrapper()方法,这里在第52行调用了bitmapDecoder.decode()方法。这个bitmapDecoder是一个ImageVideoBitmapDecoder对象,那么我们来看一下它的代码,如下所示:
ImageVideoBitmapDecoder
1 | public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> { |
在第14行先调用了source.getStream()来获取到服务器返回的InputStream,然后在第17行调用streamDecoder.decode()方法进行解码。streamDecode是一个StreamBitmapDecoder对象,那么我们再来看这个类的源码,如下所示:
StreamBitmapDecoder
1 | public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> { |
它的decode()方法又去调用了Downsampler的decode()方法。接下来又到了激动人心的时刻了,Downsampler的代码如下所示
Downsampler
1 | public abstract class Downsampler implements BitmapDecoder<InputStream> { |
对服务器返回的InputStream的读取,以及对图片的加载全都在这里了。当然这里其实处理了很多的逻辑,包括对图片的压缩,甚至还有旋转、圆角等逻辑处理,但是我们目前只需要关注主线逻辑就行了。
decode()方法执行之后,会返回一个Bitmap对象,那么图片在这里其实也就已经被加载出来了,剩下的工作就是如果让这个Bitmap显示到界面上,我们继续往下分析。
回到刚才的StreamBitmapDecoder当中,你会发现,它的decode()方法返回的是一个Resource< Bitmap>对象。
而我们从Downsampler中得到的是一个Bitmap对象,因此这里在第18行又调用了BitmapResource.obtain()方法,将Bitmap对象包装成了Resource< Bitmap>对象。代码如下所示:
BitmapResource
1 | public class BitmapResource implements Resource<Bitmap> { |
BitmapResource的源码也非常简单,经过这样一层包装之后,如果我还需要获取Bitmap,只需要调用Resource
然后我们需要一层层继续向上返回,StreamBitmapDecoder会将值返回到ImageVideoBitmapDecoder当中,而ImageVideoBitmapDecoder又会将值返回到GifBitmapWrapperResourceDecoder的decodeBitmapWrapper()方法当中。
由于代码隔得有点太远了,我重新把decodeBitmapWrapper()方法的代码贴一下:
decodeBitmapWrapper
1 | private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException { |
可以看到,decodeBitmapWrapper()方法返回的是一个GifBitmapWrapper对象。因此,这里在第5行,又将Resource< Bitmap>封装到了一个GifBitmapWrapper对象当中。这个GifBitmapWrapper顾名思义,就是既能封装GIF,又能封装Bitmap,从而保证了不管是什么类型的图片Glide都能从容应对。
我们顺便来看下GifBitmapWrapper的源码吧,如下所示:
GifBitmapWrapper
1 | public class GifBitmapWrapper { |
分别对gifResource和bitmapResource做了一层封装而已
然后这个GifBitmapWrapper对象会一直向上返回,返回到GifBitmapWrapperResourceDecoder最外层的decode()方法的时候,会对它再做一次封装,如下所示:
decode
1 | @Override |
在第11行,又将GifBitmapWrapper封装到了一个GifBitmapWrapperResource对象当中,最终返回的是一个Resource< GifBitmapWrapper>对象。这个GifBitmapWrapperResource和刚才的BitmapResource是相似的,它们都实现的Resource接口,都可以通过get()方法来获取封装起来的具体内容。
GifBitmapWrapperResource
1 | public class GifBitmapWrapperResource implements Resource<GifBitmapWrapper> { |
经过这一层的封装之后,我们从网络上得到的图片就能够以Resource接口的形式返回,并且还能同时处理Bitmap图片和GIF图片这两种情况。
现在我们可以回到DecodeJob当中了,它的decodeFromSourceData()方法返回的是一个Resource< T>对象,其实也就是Resource< GifBitmapWrapper>对象了。然后继续向上返回,最终返回到decodeFromSource()方法当中,如下所示:
1 | public Resource<Z> decodeFromSource() throws Exception { |
我们就是从这里跟进到decodeSource()方法当中,然后执行了一大堆一大堆的逻辑,最终得到了这个Resource< T>对象。
然而你会发现,decodeFromSource()方法最终返回的却是一个Resource< Z>对象,那么这到底是怎么回事呢?
transformEncodeAndTranscode
1 | private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) { |
首先,这个方法开头的几行transform还有cache,这都是我们后面才会学习的东西,现在不用管它们就可以了。需要注意的是第9行,这里调用了一个transcode()方法,就把Resource< T>对象转换成Resource< Z>对象了。
而transcode()方法中又是调用了transcoder的transcode()方法,那么这个transcoder是什么呢?其实这也是Glide源码特别难懂的原因之一,就是它用到的很多对象都是很早很早之前就初始化的,在初始化的时候你可能完全就没有留意过它,因为一时半会根本就用不着,但是真正需要用到的时候你却早就记不起来这个对象是从哪儿来的了。
那么这里我来提醒一下大家吧,在第二步load()方法返回的那个DrawableTypeRequest对象,它的构建函数中去构建了一个FixedLoadProvider对象,然后我们将三个参数传入到了FixedLoadProvider当中,其中就有一个GifBitmapWrapperDrawableTranscoder对象。后来在onSizeReady()方法中获取到了这个参数,并传递到了Engine当中,然后又由Engine传递到了DecodeJob当中。因此,这里的transcoder其实就是这个GifBitmapWrapperDrawableTranscoder对象。
GifBitmapWrapperDrawableTranscoder
1 | public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> { |
GifBitmapWrapperDrawableTranscoder的核心作用就是用来转码的。因为GifBitmapWrapper是无法直接显示到ImageView上面的,只有Bitmap或者Drawable才能显示到ImageView上。因此,这里的transcode()方法先从Resource< GifBitmapWrapper>中取出GifBitmapWrapper对象,然后再从GifBitmapWrapper中取出Resource< Bitmap>对象。
接下来做了一个判断,如果Resource< Bitmap>为空,那么说明此时加载的是GIF图,直接调用getGifResource()方法将图片取出即可,因为Glide用于加载GIF图片是使用的GifDrawable这个类,它本身就是一个Drawable对象了。而如果Resource< Bitmap>不为空,那么就需要再做一次转码,将Bitmap转换成Drawable对象才行,因为要保证静图和动图的类型一致性,不然逻辑上是不好处理的。
这里在第15行又进行了一次转码,是调用的GlideBitmapDrawableTranscoder对象的transcode()方法
GlideBitmapDrawableTranscoder
1 | public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> { |
可以看到,这里new出了一个GlideBitmapDrawable对象,并把Bitmap封装到里面。然后对GlideBitmapDrawable再进行一次封装,返回一个Resource< GlideBitmapDrawable>对象。
现在再返回到GifBitmapWrapperDrawableTranscoder的transcode()方法中,你会发现它们的类型就一致了。因为不管是静图的Resource< GlideBitmapDrawable>对象,还是动图的Resource< GifDrawable>对象,它们都是属于父类Resource< GlideDrawable>对象的。因此transcode()方法也是直接返回了Resource< GlideDrawable>,而这个Resource< GlideDrawable>其实也就是转换过后的Resource< Z>了。
那么我们继续回到DecodeJob当中,它的decodeFromSource()方法得到了Resource< Z>对象,当然也就是Resource< GlideDrawable>对象。然后继续向上返回会回到EngineRunnable的decodeFromSource()方法,再回到decode()方法,再回到run()方法当中。那么我们重新再贴一下EngineRunnable run()方法的源码:
1 | @Override |
经过第9行decode()方法的执行,我们最终得到了这个Resource< GlideDrawable>对象,那么接下来就是如何将它显示出来了。可以看到,这里在第25行调用了onLoadComplete()方法,表示图片加载已经完成了,代码如下所示:
1 | private void onLoadComplete(Resource resource) { |
这个manager就是EngineJob对象,因此这里实际上调用的是EngineJob的onResourceReady()方法
onResourceReady
1 | class EngineJob implements EngineRunnable.EngineRunnableManager { |
这里在onResourceReady()方法使用Handler发出了一条MSG_COMPLETE消息,那么在MainThreadCallback的handleMessage()方法中就会收到这条消息。从这里开始,所有的逻辑又回到主线程当中进行了,因为很快就需要更新UI了。
然后在第72行调用了handleResultOnMainThread()方法,这个方法中又通过一个循环,调用了所有ResourceCallback的onResourceReady()方法。那么这个ResourceCallback是什么呢?答案在addCallback()方法当中,它会向cbs集合中去添加ResourceCallback。那么这个addCallback()方法又是哪里调用的呢?其实调用的地方我们早就已经看过了,只不过之前没有注意,现在重新来看一下Engine的load()方法
load
1 | public class Engine implements EngineJobListener, |
在第18行上面,看到了吗?就是在这里调用的EngineJob的addCallback()方法来注册的一个ResourceCallback。那么接下来的问题就是,Engine.load()方法的ResourceCallback参数又是谁传过来的呢?这就需要回到GenericRequest的onSizeReady()方法当中了,我们看到ResourceCallback是load()方法的最后一个参数,那么在onSizeReady()方法中调用load()方法时传入的最后一个参数是什么?代码如下所示:
1 | public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback, |
请将目光锁定在第29行的最后一个参数,this。没错,就是this。GenericRequest本身就实现了ResourceCallback的接口,因此EngineJob的回调最终其实就是回调到了GenericRequest的onResourceReady()方法当中了
onResourceReady
1 | public void onResourceReady(Resource<?> resource) { |
这里有两个onResourceReady()方法,首先在第一个onResourceReady()方法当中,调用resource.get()方法获取到了封装的图片对象,也就是GlideBitmapDrawable对象,或者是GifDrawable对象。然后将这个值传入到了第二个onResourceReady()方法当中,并在第36行调用了target.onResourceReady()方法。
那么这个target又是什么呢?这个又需要向上翻很久了,在第三步into()方法的一开始,我们就分析了在into()方法的最后一行,调用了glide.buildImageViewTarget()方法来构建出一个Target,而这个Target就是一个GlideDrawableImageViewTarget对象。
GlideDrawableImageViewTarget
1 | public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> { |
在GlideDrawableImageViewTarget的onResourceReady()方法中做了一些逻辑处理,包括如果是GIF图片的话,就调用resource.start()方法开始播放图片,但是好像并没有看到哪里有将GlideDrawable显示到ImageView上的逻辑。
确实没有,不过父类里面有,这里在第25行调用了super.onResourceReady()方法,GlideDrawableImageViewTarget的父类是ImageViewTarget,我们来看下它的代码吧:
1 | public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter { |
可以看到,在ImageViewTarget的onResourceReady()方法当中调用了setResource()方法,而ImageViewTarget的setResource()方法是一个抽象方法,具体的实现还是在子类那边实现的。
那子类的setResource()方法是怎么实现的呢?回头再来看一下GlideDrawableImageViewTarget的setResource()方法,没错,调用的view.setImageDrawable()方法,而这个view就是ImageView。代码执行到这里,图片终于也就显示出来了。
那么,我们对Glide执行流程的源码分析,到这里也终于结束了。