Glide源码导读

hopetoutou 8年前
   <p>最近比较无聊,为了找点事干,就花了两天时间把Glide的源码大概看了一下。刚开始看Glide的源码头脑还是比较乱的,因为作者引入了几个概念,又大量用了泛型,如果不了解这些概念读起代码来就比较痛苦,我也没有详细看各种实现细节的东西,只是了解了下这个框架的大概样子,在这篇文章里,我会介绍下Glide中的一些关键概念,并走一遍图片加载流程,如果你要阅读Glide源码的话,应该多少会有点帮助。</p>    <h2><strong>基本概念</strong></h2>    <p>首先是三个最基本的概念: Model , Data 和 Resource 。</p>    <p>想一下,我们加载图片需要什么?一般是一个url,但url并不是所有情况,还有资源ID,文件等等,甚至可以是Feed流中的一条Feed,虽然一般我们会从Feed中取出图片的url来转换为从url中加载的情况,Glide把这些抽像为了一个概念,就是 Model ,所以 Model 就是数据地址的最初来源。</p>    <p>Model 并不能直接解析为图片,比如一个url,是要转换为网络流的InputStream才能被解析为图片的, Model 需要进行一次转换才能做为数据解析的数据源,这些转换后的东西就叫做 Data ,Glide并没有一个Data类,但有很多和它相关的概念,如dataClase,DataFetcher等。</p>    <p>那么 Resource 呢,其实它就是一个包装类,一个wrapper,它wrap一个对象,使这个对象可以通过对象池进行缓存与重用。</p>    <p>这三个基本概念介绍完了,接下来看一下Glide基本框架。</p>    <p>做为一个图片加载框架,肯定会包含缓存部分。</p>    <p>可以从网上很容易的了解到,Glide的磁盘缓存可以缓存原始数据,也可以缓存处理过的数据。什么意思呢,就是你有一张1000x1000的图片,但你是在列表中展示的,比如是200x200,那么缓存时可以直接将整个网络流缓存下来,即1000x1000的图片,要展示的时候再缩放,但这就降低了展示效率,所以Glide也可以把处理过的200x200的图片缓存起来,增加了缓存大小,但优化了展示速度。</p>    <p>至于怎么把数据缓存到磁盘,就引入了一个叫 Encoder 的概念, Encoder 是用来持久化数据的。</p>    <p>但看源码时你会发现,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 对应。</p>    <p>有 Encoder 就有 Decoder ,对应的类叫 ResourceDecoder ,用来将数据(InputStream等)解析为 Resource 。</p>    <p>图片加载出来后还可能会应用各种变换,如圆角图片,圆形图片,处理这部分工作的叫 Transformation</p>    <p>基础概念介绍的差不多了,加载流程也差不多出来了:</p>    <p><img src="https://simg.open-open.com/show/fffb507e8b1852bb36b4685be54dd885.png"></p>    <p>但我们发现前面的介绍中少了一环,即:Glide是怎么把 Model 转换为 Data 的。这就引入另一个概念, ModelLoader ,就是把 Model 转换成 Data 的,为了方便说明,直接把这个类的代码贴上来了,去掉了一些注释。</p>    <pre>  <code class="language-java">/**  * 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);  }  </code></pre>    <p>ModelLoader 有两个方法,一个 handles 表示是否可以处理这个类型的 Model ,如果可以的话就可以通过 buildLoadData 生成一个 LoadData ,而 LoadData 包含了要用来做缓存的key,及用来获取数据的 DataFetcher 。</p>    <p>到这里,整个加载流程就清楚了:</p>    <p><img src="https://simg.open-open.com/show/1c5212779b04dc66937018174dc60097.png"></p>    <h2><strong>基本加载流程</strong></h2>    <p>接下来要做的就是根据我们的使用方法走一遍流程,调用如下:</p>    <pre>  <code class="language-java">Glide.with(mContext)      .load(url)      .apply(RequestOptions.placeholderOf(R.drawable.loading))      .into(myImageView);  </code></pre>    <p>一步步看,先是 Glide.with(mContext) :</p>    <pre>  <code class="language-java">public static RequestManager with(Context context) {   RequestManagerRetriever retriever = RequestManagerRetriever.get();   return retriever.get(context);  }  </code></pre>    <p>通过 RequestManagerRetriever 获取到了一个 RequestManager ,至于为什么还需要一个 RequestManagerRetriever 并有各种重载方法,主要是因为Glide通过 SupportRequestManagerFragment 和 RequestManagerFragment 关联了Activity或Fragment的生命周期,用来做 pauseRequests 等操作。</p>    <p>然后是 load :</p>    <pre>  <code class="language-java">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);  }  </code></pre>    <p>是 asDrawable.load(model) 的缩写,就是说这个Model我是要加载为Drawable的,最终返回一个 RequestBuilder ,看名字就知道是做什么了,不过这个类主要是设置Thumbnail Request,Transition等个别设置(旧版本中placeHolder等也是在这里设置的),大部分设置在 RequestOptions 里,这就是下面这一句:</p>    <pre>  <code class="language-java">apply(RequestOptions.placeholderOf(R.drawable.loading))  </code></pre>    <p>应用一个 RequestOptions , RequestOptions 可以设置各种请求相关的选项,如占位图片,加载失败的图片,缓存策略等。 RequestOptions 继承自 BaseRequestOptions ,但全是工厂方法生成各种RequestOptions。</p>    <p>最后就是 into 了,把图片加载到一个 Target 中。</p>    <pre>  <code class="language-java">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;  }  </code></pre>    <p>Target 是要加载到的目标,比如 ImageViewTarget , AppWidgetTarget ,在这里我们传进来了一个 ImageView ,内部生成了一个 DrawableImageViewTarget 。这里最主要的操作是 buildRequest 然后交给 RequestManager 去 track 。</p>    <pre>  <code class="language-java">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);   }  }  </code></pre>    <p>TargetTracker 主要就是记录一下所有正在加载的图片的 Target ,所以加载行为是在 RequestTracker.runRequest 中的, runRequest 先判断是否是pause状态(RequestManager设置),如果不是就直接调用 Request.begin 触发加载,否则就回到pending队列里等待resume。</p>    <p>除了设置缩略图的情景,使用的 Request 都是 SingleRequest ,看一下它的 begin 方法:</p>    <pre>  <code class="language-java">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));   }  }  </code></pre>    <p>加载逻辑是这几行:</p>    <pre>  <code class="language-java">if (Util.isValidDimensions(overrideWidth, overrideHeight)) {    onSizeReady(overrideWidth, overrideHeight);  } else {    target.getSize(this);  }  </code></pre>    <p>判断下是否知道 Target 的大小,如果大小已知就调用 onSizeReady ,否则就调用 target.getSize 获取它的大小,当成功获取到大小后,会通过回调继续调用 onSizeReady ,所以整个加载方法都是在 onSizeReady 里的。至于 Target 怎么获取它的大小,那要看它的实现了,对于 ImageViewTarget ,是通过 ViewTreeObserver.OnPreDrawListener 等到View要测绘的时候就知道它的大小了。</p>    <p>onSizeReady 就是把操作转移到了 Engine.load</p>    <pre>  <code class="language-java">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);  }  </code></pre>    <p>在 Engine.load 中,先 loadFromCache ,如果缓存没有命中就再 loadFromActiveResources ,这是两级内存缓存,第一级是LruCache,第二级是ActiveCache,主要作用是,有可能一个图片很早就被加载了,可能已经从LruCache被移除掉了,但这个图片可能还在被某一个地方引用着,也就是还是Active的,那它就可能在将来仍被引用到,所以就把它保留在二级的ActiveCache中,ActiveCache中是以弱引用引用图片的,并通过 ReferenceQueue 监测弱引用的回收,然后用 Handler.IdleHandler 在CPU空闲时被被回收的引用项从ActiveCache中移除。</p>    <p>接下来看对应的Key是否已经正在加载,如果是的话,就 addCallback ,这样如果有多个地方同时请求同一张图片的话,只会生成一个加载任务,并都能收到回调,这点是比Universal-Image-Loader好的地方。</p>    <p>正常的加载流程是生成一个 EngineJob 和一个 DecodeJob ,通过 engineJob.start(decodeJob) 来进行实际的加载。</p>    <pre>  <code class="language-java">public void start(DecodeJob<R> decodeJob) {   this.decodeJob = decodeJob;   GlideExecutor executor = decodeJob.willDecodeFromCache()       ? diskCacheExecutor       : getActiveSourceExecutor();   executor.execute(decodeJob);  }  </code></pre>    <p>EngineJob.start 直接将 DecodeJob 交给Executor去执行了( DecodeJob 实现了 Runnable 接口)。 DecodeJob 的加载操作放到了 runWrapped 中</p>    <pre>  <code class="language-java">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);   }  }  </code></pre>    <p>主要加载逻辑就在这三个函数中了:</p>    <ol>     <li>先获取当前的Stage</li>     <li>根据当前的Stage获取相应的Generator,</li>     <li>执行Generator</li>    </ol>    <p>一共有三种Generator:</p>    <ul>     <li>ResourceCacheGenerator :从处理过的缓存加载数据</li>     <li>DataCacheGenerator :从原始缓存加载数据</li>     <li>SourceGenerator :从数据源请求数据,如网络请求</li>    </ul>    <p>前面说过,Glide的磁盘缓存可以选择缓存原始图片,缓存处理过的图片(如列表中显示缩略图时缩放后的图片),这三个Generator就分别对应处理过的图片缓存,原始图片缓存,和数据源加载。</p>    <p>在上面的第三步执行Generator时主要就是调用了Generator,其实就是执行Generator的 startNext 方法,这里以 SourceGenerator 为例。</p>    <pre>  <code class="language-java">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;  }  </code></pre>    <p>先忽略函数开始时 dataToCache 和 sourceCacheGenerator 相关的代码,第一次加载时这两个一定是null的。剩下的流程就是获取一个 LoadData ,调用 LoadData.fetcher.loadData 加载数据。看一下 LoadData</p>    <pre>  <code class="language-java">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;  }  </code></pre>    <p>在 getLoadData 中通过获取所有提前注册过的能处理 Model 类型的 ModelLoader ,调用它的 buildLoadData 生成 LoadData ,最终返回一个 LoadData 列表。</p>    <p>前面说过 LoadData 包含了用来获取数据的 DataFetcher 。 SourceGenerator.startNext 就调用了 loadData.fetcher.loadData 来进行加载数据,并传进去一个Callback,就是当前的 SourceGenerator ,如果加载成功,会调用 onDataReady</p>    <pre>  <code class="language-java">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);   }  }  </code></pre>    <p>数据加载成功后,如果设置了要进行磁盘缓存,会设置成员变量 dataToCache ,并调用Callback的 reschedule ,结果就是会再次调用当前Generator的 startNext , startNext 的前半部分实现就起作用了,会进行写缓存的操作。</p>    <p>当 rescheudle 后写了缓存后,或不缓存的情况下,会调用 onDataFetcherReady ,这个Callback就是前面的 DecodeJob ,在 onDataFetcherReady 中会调用 decodeFromRetrievedData decode数据,最终调用到 decodeFromFetcher</p>    <pre>  <code class="language-java">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);  }  </code></pre>    <p>获取 LoadPath ,并调用它的 load 方法。 LoadPath 就是封装了多个 DecodePath , DecodePath 用于decode and Transform数据,如InputStream->Bitmap->BitmapDrawable, DecodePath 中会获取预先注册的 Decoder 来decode获取到的数据,decode成功后通过回调调用 DecodeJob 的 onResourceDecoded 方法。</p>    <pre>  <code class="language-java">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;  }  </code></pre>    <p>在上述代码的注释1处对加载成功的资源应用Transformation,然后在注释2处根据缓存策略初始化 DeferredEncodeManager ,在前面的 decodeFromRetrievedData 中,如果有必要会把transform过的资源写缓存。</p>    <pre>  <code class="language-java">private void decodeFromRetrievedData() {    ...     if (resource != null) {     notifyEncodeAndRelease(resource, currentDataSource);   } else {     runGenerators();   }  }  </code></pre>    <p>notifyEncodeAndRelease 中处理了对处理过的图片的缓存操作。当缓存完成后(如果有需要的话)就通过回调告诉外面加载完成了。至此,整个加载过程完成。</p>    <h2><strong>Glide配置</strong></h2>    <p>Glide允许我们进行一定程度的自定义,比如设置自定义的Executor,设置缓存池,设置Log等级等,完成这个任务的类叫 GlideBuilder ,Glide类在工程中是作为单例使用的,看一下代码:</p>    <pre>  <code class="language-java">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;  }  </code></pre>    <p>通过 GlideBuilder 生成了一个 Glide 实例,我们是没有办法直接配置 GlideBuilder 的,但我们发现 Glide.get 解析了Manifest,获取了一个 GlideModule 的列表,并调用了它的 applyOptions 和 registerComponents 方法。以项目中OkHttp的配置为例</p>    <pre>  <code class="language-java">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());   }  }  </code></pre>    <p>GlideModule 有两个方法, applyOptions ,有一个 GlideBuilder 参数,在这里我们就可以配置Glide了。还有一个 registerComponents 方法,并有一个 Registry 参数,通过这个类的实例我们就可以注册我们自定义的 ModelLoader , Encoder 等基础组件了。</p>    <p>自定义 GlideModule 是通过Manifest的meta-data标签配置的</p>    <pre>  <code class="language-java"><meta-data   android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule"   android:value="GlideModule"/>  </code></pre>    <h2><strong>参考资料</strong></h2>    <p><a href="/misc/goto?guid=4959714154494877964" rel="nofollow,noindex">http://www.lightskystreet.com/2015/10/12/glide_source_analysis/</a></p>    <p> </p>    <p>来自:http://www.angeldevil.me/2016/09/05/glide/</p>    <p> </p>