最近比较无聊,为了找点事干,就花了两天时间把Glide的源码大概看了一下。刚开始看Glide的源码头脑仍是比较乱的,由于做者引入了几个概念,又大量用了泛型,若是不了解这些概念读起代码来就比较痛苦,我也没有详细看各类实现细节的东西,只是了解了下这个框架的大概样子,在这篇文章里,我会介绍下Glide中的一些关键概念,并走一遍图片加载流程,若是你要阅读Glide源码的话,应该多少会有点帮助。java
首先是三个最基本的概念:Model
, Data
和Resource
。android
想一下,咱们加载图片须要什么?通常是一个url,但url并非全部状况,还有资源ID,文件等等,甚至能够是Feed流中的一条Feed,虽然通常咱们会从Feed中取出图片的url来转换为从url中加载的状况,Glide把这些抽像为了一个概念,就是Model
,因此Model
就是数据地址的最初来源。缓存
Model
并不能直接解析为图片,好比一个url,是要转换为网络流的InputStream才能被解析为图片的,Model
须要进行一次转换才能作为数据解析的数据源,这些转换后的东西就叫作Data
,Glide并无一个Data类,但有不少和它相关的概念,如dataClase,DataFetcher等。网络
那么Resource
呢,其实它就是一个包装类,一个wrapper,它wrap一个对象,使这个对象能够经过对象池进行缓存与重用。app
这三个基本概念介绍完了,接下来看一下Glide基本框架。框架
作为一个图片加载框架,确定会包含缓存部分。ide
能够从网上很容易的了解到,Glide的磁盘缓存能够缓存原始数据,也能够缓存处理过的数据。什么意思呢,就是你有一张1000x1000的图片,但你是在列表中展现的,好比是200x200,那么缓存时能够直接将整个网络流缓存下来,即1000x1000的图片,要展现的时候再缩放,但这就下降了展现效率,因此Glide也能够把处理过的200x200的图片缓存起来,增长了缓存大小,但优化了展现速度。函数
至于怎么把数据缓存到磁盘,就引入了一个叫Encoder
的概念,Encoder
是用来持久化数据的。fetch
但看源码时你会发现,Glide中有一个类叫Registry
,能够注册多个Encoder
,但你会发现它还能够注册ResourceEncoder
。这两个Encoder
很容易引发混淆,而其实ResouseEncoder
继承自Encoder
。Encoder
是用来持久化Data
的,ResourceEncoder
是用来持久化Resource
的。看Glide默认注册的Encoder
就知道了,默认注册的Encoder
为ByteBuffer
和InputStream
,而ResourceEncoder
是Bitmap
、BitmapDrawable
和GifDrawable
,也就是一个持久化原始数据,一个持久化处理过的数据。我感受把Encoder
作为一个上级的抽象,引入一个和ResourceEncoder
同级的DataEncoder
就好理解了,正好和前面的基本概念Data
和Resource
对应。优化
有Encoder
就有Decoder
,对应的类叫ResourceDecoder
,用来将数据(InputStream等)解析为Resource
。
图片加载出来后还可能会应用各类变换,如圆角图片,圆形图片,处理这部分工做的叫Transformation
基础概念介绍的差很少了,加载流程也差很少出来了:
但咱们发现前面的介绍中少了一环,即:Glide是怎么把Model
转换为Data
的。这就引入另外一个概念,ModelLoader
,就是把Model
转换成Data
的,为了方便说明,直接把这个类的代码贴上来了,去掉了一些注释。
/** * A factory interface for translating an arbitrarily complex data model into a concrete data type * that can be used by an {@link DataFetcher} to obtain the data for a resource represented by the * model. * * @param <Model> The type of the model. * @param <Data> The type of the data that can be used by a * {@link com.bumptech.glide.load.ResourceDecoder} to decode a resource. */ public interface ModelLoader<Model, Data> { /** * Contains a set of {@link com.bumptech.glide.load.Key Keys} identifying the source of the load, * alternate cache keys pointing to equivalent data, and a * {@link com.bumptech.glide.load.data.DataFetcher} that can be used to fetch data not found in * cache. * * @param <Data> The type of data that well be loaded. */ class LoadData<Data> { public final Key sourceKey; public final List<Key> alternateKeys; public final DataFetcher<Data> fetcher; public LoadData(Key sourceKey, DataFetcher<Data> fetcher) { this(sourceKey, Collections.<Key>emptyList(), fetcher); } public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) { this.sourceKey = Preconditions.checkNotNull(sourceKey); this.alternateKeys = Preconditions.checkNotNull(alternateKeys); this.fetcher = Preconditions.checkNotNull(fetcher); } } LoadData<Data> buildLoadData(Model model, int width, int height, Options options); boolean handles(Model model); }
ModelLoader
有两个方法,一个handles
表示是否能够处理这个类型的Model
,若是能够的话就能够经过buildLoadData
生成一个LoadData
,而LoadData
包含了要用来作缓存的key,及用来获取数据的DataFetcher
。
到这里,整个加载流程就清楚了:
接下来要作的就是根据咱们的使用方法走一遍流程,调用以下:
Glide.with(mContext) .load(url) .apply(RequestOptions.placeholderOf(R.drawable.loading)) .into(myImageView);
一步步看,先是Glide.with(mContext)
:
public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); }
经过RequestManagerRetriever
获取到了一个RequestManager
,至于为何还须要一个RequestManagerRetriever
并有各类重载方法,主要是由于Glide经过SupportRequestManagerFragment
和RequestManagerFragment
关联了Activity或Fragment的生命周期,用来作pauseRequests
等操做。
而后是load
:
public RequestBuilder<Drawable> load(@Nullable Object model) { return asDrawable().load(model); } public RequestBuilder<Drawable> asDrawable() { return as(Drawable.class).transition(new DrawableTransitionOptions()); } public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide.getGlideContext(), this, resourceClass); }
是asDrawable.load(model)
的缩写,就是说这个Model我是要加载为Drawable的,最终返回一个RequestBuilder
,看名字就知道是作什么了,不过这个类主要是设置Thumbnail Request,Transition等个别设置(旧版本中placeHolder等也是在这里设置的),大部分设置在RequestOptions
里,这就是下面这一句:
apply(RequestOptions.placeholderOf(R.drawable.loading))
应用一个RequestOptions
,RequestOptions
能够设置各类请求相关的选项,如占位图片,加载失败的图片,缓存策略等。RequestOptions
继承自BaseRequestOptions
,但全是工厂方法生成各类RequestOptions。
最后就是into
了,把图片加载到一个Target
中。
public Target<TranscodeType> into(ImageView view) { ... return into(context.buildImageViewTarget(view, transcodeClass)); } public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } Request previous = target.getRequest(); if (previous != null) { requestManager.clear(target); } requestOptions.lock(); Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request); return target; }
Target
是要加载到的目标,好比ImageViewTarget
,AppWidgetTarget
,在这里咱们传进来了一个ImageView
,内部生成了一个DrawableImageViewTarget
。这里最主要的操做是buildRequest
而后交给RequestManager
去track
。
void track(Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); } // RequestTracker public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
TargetTracker
主要就是记录一下全部正在加载的图片的Target
,因此加载行为是在RequestTracker.runRequest
中的,runRequest
先判断是不是pause状态(RequestManager设置),若是不是就直接调用Request.begin
触发加载,不然就回到pending队列里等待resume。
除了设置缩略图的情景,使用的Request
都是SingleRequest
,看一下它的begin
方法:
public void begin() { stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); if (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. int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG; onLoadFailed(new GlideException("Received null model"), logLevel); return; } status = Status.WAITING_FOR_SIZE; 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()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }
加载逻辑是这几行:
if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); }
判断下是否知道Target
的大小,若是大小已知就调用onSizeReady
,不然就调用target.getSize
获取它的大小,当成功获取到大小后,会经过回调继续调用onSizeReady
,因此整个加载方法都是在onSizeReady
里的。至于Target
怎么获取它的大小,那要看它的实现了,对于ImageViewTarget
,是经过ViewTreeObserver.OnPreDrawListener
等到View要测绘的时候就知道它的大小了。
onSizeReady
就是把操做转移到了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, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } EngineJob<?> current = jobs.get(key); if (current != null) { current.addCallback(cb); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(decodeJob); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }
在Engine.load
中,先loadFromCache
,若是缓存没有命中就再loadFromActiveResources
,这是两级内存缓存,第一级是LruCache,第二级是ActiveCache,主要做用是,有可能一个图片很早就被加载了,可能已经从LruCache被移除掉了,但这个图片可能还在被某一个地方引用着,也就是仍是Active的,那它就可能在未来仍被引用到,因此就把它保留在二级的ActiveCache中,ActiveCache中是以弱引用引用图片的,并经过ReferenceQueue
监测弱引用的回收,而后用Handler.IdleHandler
在CPU空闲时被被回收的引用项从ActiveCache中移除。
接下来看对应的Key是否已经正在加载,若是是的话,就addCallback
,这样若是有多个地方同时请求同一张图片的话,只会生成一个加载任务,并都能收到回调,这点是比Universal-Image-Loader好的地方。
正常的加载流程是生成一个EngineJob
和一个DecodeJob
,经过engineJob.start(decodeJob)
来进行实际的加载。
public void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
EngineJob.start
直接将DecodeJob
交给Executor去执行了(DecodeJob
实现了Runnable
接口)。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); } } 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); } } 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: return Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException("Unrecognized stage: " + current); } }
主要加载逻辑就在这三个函数中了:
一共有三种Generator:
ResourceCacheGenerator
:从处理过的缓存加载数据DataCacheGenerator
:从原始缓存加载数据SourceGenerator
:从数据源请求数据,如网络请求前面说过,Glide的磁盘缓存能够选择缓存原始图片,缓存处理过的图片(如列表中显示缩略图时缩放后的图片),这三个Generator就分别对应处理过的图片缓存,原始图片缓存,和数据源加载。
在上面的第三步执行Generator时主要就是调用了Generator,其实就是执行Generator的startNext
方法,这里以SourceGenerator
为例。
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; }
先忽略函数开始时dataToCache
和sourceCacheGenerator
相关的代码,第一次加载时这两个必定是null的。剩下的流程就是获取一个LoadData
,调用LoadData.fetcher.loadData
加载数据。看一下LoadData
List<LoadData<?>> getLoadData() { if (!isLoadDataSet) { isLoadDataSet = true; loadData.clear(); List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); int size = modelLoaders.size(); for (int i = 0; i < size; i++) { ModelLoader<Object, ?> modelLoader = modelLoaders.get(i); LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); if (current != null) { loadData.add(current); } } } return loadData; }
在getLoadData
中经过获取全部提早注册过的能处理Model
类型的ModelLoader
,调用它的buildLoadData
生成LoadData
,最终返回一个LoadData
列表。
前面说过LoadData
包含了用来获取数据的DataFetcher
。SourceGenerator.startNext
就调用了loadData.fetcher.loadData
来进行加载数据,并传进去一个Callback,就是当前的SourceGenerator
,若是加载成功,会调用onDataReady
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
,并调用Callback的reschedule
,结果就是会再次调用当前Generator的startNext
,startNext
的前半部分实现就起做用了,会进行写缓存的操做。
当rescheudle
后写了缓存后,或不缓存的状况下,会调用onDataFetcherReady
,这个Callback就是前面的DecodeJob
,在onDataFetcherReady
中会调用decodeFromRetrievedData
decode数据,最终调用到decodeFromFetcher
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException { LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); return runLoadPath(data, dataSource, path); }
获取LoadPath
,并调用它的load
方法。LoadPath
就是封装了多个DecodePath
,DecodePath
用于decode and Transform数据,如InputStream->Bitmap->BitmapDrawable,DecodePath
中会获取预先注册的Decoder
来decode获取到的数据,decode成功后经过回调调用DecodeJob
的onResourceDecoded
方法。
public Resource<Z> onResourceDecoded(Resource<Z> decoded) { Class<Z> resourceSubClass = getResourceClass(decoded); Transformation<Z> appliedTransformation = null; Resource<Z> transformed = decoded; if (dataSource != DataSource.RESOURCE_DISK_CACHE) { appliedTransformation = decodeHelper.getTransformation(resourceSubClass); transformed = appliedTransformation.transform(decoded, width, height); ////////////////////////// 1 } // TODO: Make this the responsibility of the Transformation. if (!decoded.equals(transformed)) { decoded.recycle(); } final EncodeStrategy encodeStrategy; final ResourceEncoder<Z> encoder; if (decodeHelper.isResourceEncoderAvailable(transformed)) { encoder = decodeHelper.getResultEncoder(transformed); encodeStrategy = encoder.getEncodeStrategy(options); } else { encoder = null; encodeStrategy = EncodeStrategy.NONE; } Resource<Z> result = transformed; boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, encodeStrategy)) { if (encoder == null) { throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); } final Key key; if (encodeStrategy == EncodeStrategy.SOURCE) { key = new DataCacheKey(currentSourceKey, signature); } else if (encodeStrategy == EncodeStrategy.TRANSFORMED) { key = new ResourceCacheKey(currentSourceKey, signature, width, height, appliedTransformation, resourceSubClass, options); } else { throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy); } LockedResource<Z> lockedResult = LockedResource.obtain(transformed); deferredEncodeManager.init(key, encoder, lockedResult); ////////////////////////// 2 result = lockedResult; } return result; }
在上述代码的注释1处对加载成功的资源应用Transformation,而后在注释2处根据缓存策略初始化DeferredEncodeManager
,在前面的decodeFromRetrievedData
中,若是有必要会把transform过的资源写缓存。
private void decodeFromRetrievedData() { ... if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
notifyEncodeAndRelease
中处理了对处理过的图片的缓存操做。当缓存完成后(若是有须要的话)就经过回调告诉外面加载完成了。至此,整个加载过程完成。
Glide容许咱们进行必定程度的自定义,好比设置自定义的Executor,设置缓存池,设置Log等级等,完成这个任务的类叫GlideBuilder
,Glide类在工程中是做为单例使用的,看一下代码:
public static Glide get(Context context) { if (glide == null) { synchronized (Glide.class) { if (glide == null) { Context applicationContext = context.getApplicationContext(); List<GlideModule> modules = new ManifestParser(applicationContext).parse(); GlideBuilder builder = new GlideBuilder(applicationContext); for (GlideModule module : modules) { module.applyOptions(applicationContext, builder); } glide = builder.createGlide(); for (GlideModule module : modules) { module.registerComponents(applicationContext, glide.registry); } } } } return glide; }
经过GlideBuilder
生成了一个Glide
实例,咱们是没有办法直接配置GlideBuilder
的,但咱们发现Glide.get
解析了Manifest,获取了一个GlideModule
的列表,并调用了它的applyOptions
和registerComponents
方法。以项目中OkHttp的配置为例
public class OkHttpGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // Do nothing. } @Override public void registerComponents(Context context, Registry registry) { registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); } }
GlideModule
有两个方法,applyOptions
,有一个GlideBuilder
参数,在这里咱们就能够配置Glide了。还有一个registerComponents
方法,并有一个Registry
参数,经过这个类的实例咱们就能够注册咱们自定义的ModelLoader
,Encoder
等基础组件了。
自定义GlideModule
是经过Manifest的meta-data标签配置的
<meta-data android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule" android:value="GlideModule"/>
http://www.lightskystreet.com/2015/10/12/glide_source_analysis/