Glide 大体流程分析
基于 Glide - 4.9 版本
总体分析路径
Glide.with(context).load("路径").into(imageView);
首先分析主要的流程,即从 Glide 初始化开始,网络请求的构建,最终的为目标设置图片资源。待主要流程分析完毕后,在按照区分析其中的重点以及优秀设计点。
- 缓存机制
- 灵活的配置机制
分析整体步骤
Glide.with() 初始化
public static RequestManager with(@NonNull Context context) {
// 这里有两步,第一步获取RequestManagerRetriever,第二步获取 RequestManager
return getRetriever(context).get(context);
}
-
获取 RequestManagerRetriever
private static RequestManagerRetriever getRetriever(@Nullable Context context) { // 初始化 glide,并获取其中的 RequestManagerRestriever return Glide.get(context).getRequestManagerRetriever(); } @GuardedBy("Glide.class") private static void initializeGlide( @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) { initializeGlide(context, new GlideBuilder(), generatedAppGlideModule); } // Gilde.get() 方法最终调用了该方法初始化了 Glide @GuardedBy("Glide.class") @SuppressWarnings("deprecation") private static void initializeGlide( @NonNull Context context, @NonNull GlideBuilder builder, @Nullable GeneratedAppGlideModule annotationGeneratedModule) { Context applicationContext = context.getApplicationContext(); List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList(); // 将 Manifest.xml 文件中定义的 AppModule 迁移到注解生成的 AppModule 中 if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) { manifestModules = new ManifestParser(applicationContext).parse(); } // 去除重复 Manifest.xml 和注解处理器生成的重复的 AppModule if (annotationGeneratedModule != null && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) { Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses(); Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator(); while (iterator.hasNext()) { com.bumptech.glide.module.GlideModule current = iterator.next(); if (!excludedModuleClasses.contains(current.getClass())) { continue; } if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current); } iterator.remove(); } } if (Log.isLoggable(TAG, Log.DEBUG)) { for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) { Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass()); } } // 从 Module 中获取 RequestManagerFactory,如果没有则设置为空,TODO 1.查找 requestManagerFactory 默认在哪里设置,理解该机制 RequestManagerRetriever.RequestManagerFactory factory = annotationGeneratedModule != null ? annotationGeneratedModule.getRequestManagerFactory() : null; builder.setRequestManagerFactory(factory); // 对各 module 应用 option for (com.bumptech.glide.module.GlideModule module : manifestModules) { module.applyOptions(applicationContext, builder); } if (annotationGeneratedModule != null) { annotationGeneratedModule.applyOptions(applicationContext, builder); } // 使用 GlideBuilder build 建立 Glider,会设置一系列的默认选项 Glide glide = builder.build(applicationContext); // 对各个 Module 注册组件 for (com.bumptech.glide.module.GlideModule module : manifestModules) { try { module.registerComponents(applicationContext, glide, glide.registry); } catch (AbstractMethodError e) { throw new IllegalStateException( "Attempting to register a Glide v3 module. If you see this, you or one of your" + " dependencies may be including Glide v3 even though you're using Glide v4." + " You'll need to find and remove (or update) the offending dependency." + " The v3 module name is: " + module.getClass().getName(), e); } } if (annotationGeneratedModule != null) { annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry); } applicationContext.registerComponentCallbacks(glide); Glide.glide = glide; } // 而后从 Glide 中获取 RequestManagerRestriever public RequestManagerRetriever getRequestManagerRetriever() { return requestManagerRetriever; }
-
通过 RequestManagerRestriever 获取 RequestManager
在这一步中我们要注意一点,这里通过向 Activity 或 Frgment 添加了一个无视图的 Fragment ,然后通过其相应的生命周期去监控对应 UI 页面的生命周期变动,动态的去控制请求的开始与取消。
public RequestManager get(@NonNull Context context) { if (context == null) { throw new IllegalArgumentException("You cannot start a load on a null Context"); } else if (Util.isOnMainThread() && !(context instanceof Application)) { // 一般都是走的这边 if (context instanceof FragmentActivity) { return get((FragmentActivity) context); } else if (context instanceof Activity) { return get((Activity) context); } else if (context instanceof ContextWrapper // Only unwrap a ContextWrapper if the baseContext has a non-null application context. // Context#createPackageContext may return a Context without an Application instance, // in which case a ContextWrapper may be used to attach one. && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { return get(((ContextWrapper) context).getBaseContext()); } } // 如果是在后台执行(如:在Service中执行,或者在 Application 中调用),那么直接获取基于 ApplocationContext 的 Manager return getApplicationManager(context); } // 最终调用到了这里返回了 RequestManager private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) { // 添加一个无视图的 Fragment 以便监控视图的状态好管理请求(一个优秀的设计,这样子在 Activity 和 Fragment 中的请求管理就统一了) // TODO 这里需要研究是是如何通过 Fragment 的生命周期管理连接请求的 SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint, isParentVisible); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { // TODO(b/27524013): Factor out this Glide.get() call. Glide glide = Glide.get(context); requestManager = factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); current.setRequestManager(requestManager); } return requestManager; }
-
小结
上述的操作主要就是完成
Glide
的初始化工作以及生成RequestManager
为后续的请求构建做准备。同时这里有几点需要注意:- 这里对于
AppModule
的使用需要注意。 - 这里对于加载数据的请求设计要好好思考。
- 这里对于
Glide.load() 构建数据加载请求
-
创建指定类型的 RequestBuilder ,以便后期进行数据解码时使用
public RequestBuilder<Drawable> load(@Nullable String string) {
// 首先,通过一系列的 as 方法(请求完成后自动将结果转换成设定的图片格式)创建请求 Builder
// 其次,通过 Builder 设置请求的地址
return asDrawable().load(string);
}
// 这里使用泛型方法创建指定的 RequestBulder
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
-
设置加载数据的路径
通过对这个路径的判断,可以得出后面使用哪种数据解码模型对加载成功后的数据进行解码操作。
public RequestBuilder<TranscodeType> load(@Nullable String string) { return loadGeneric(string); } // 这里注意,在后面转码的时候会用到 private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; }
-
小结
这一步的
RequestBuilder
的构建需要注意的是- 在构建的时候传入的数据解码 Module Object。
- 构建时对于
TransitionOption
的使用。
into() 请求数据、解码、为目标设置数据
-
首先调用
RequestManager
的into
方法// 前面有个转调用 private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) { Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } // 构建请求,注意看!!!!! Request request = buildRequest(target, targetListener, options, callbackExecutor); // 一个小小的缓存机制 Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { // If the request is completed, beginning again will ensure the result is re-delivered, // triggering RequestListeners and Targets. If the request is failed, beginning again will // restart the request, giving it another chance to complete. If the request is already // running, we can let it continue running without interruption. if (!Preconditions.checkNotNull(previous).isRunning()) { // Use the previous request rather than the new one to allow for optimizations like skipping // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions // that are done in the individual Request. previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); // 执行请求 requestManager.track(target, request); return target; }
-
buildRequest 的构建
分析这一步很重要,这一步有请求的构建
private Request buildRequestRecursive( Object requestLock, Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) { // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator. ErrorRequestCoordinator errorRequestCoordinator = null; // 在设置了加载失败的 RequestBuilder 情况下会走这一步,构建一个请求的协调员 if (errorBuilder != null) { errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator); parentCoordinator = errorRequestCoordinator; } // 构建主要的请求,注意!!!这列含有缩略图逻辑!!!!!下面有具体分析 Request mainRequest = buildThumbnailRequestRecursive( requestLock, target, targetListener, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, requestOptions, callbackExecutor); // 如果没有设置加载失败后执行的请求就直接返回主要的请求 if (errorRequestCoordinator == null) { return mainRequest; } int errorOverrideWidth = errorBuilder.getOverrideWidth(); int errorOverrideHeight = errorBuilder.getOverrideHeight(); if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) { errorOverrideWidth = requestOptions.getOverrideWidth(); errorOverrideHeight = requestOptions.getOverrideHeight(); } // 构建请求出错失败了之后在进行的新的请求 Request errorRequest = errorBuilder.buildRequestRecursive( requestLock, target, targetListener, errorRequestCoordinator, errorBuilder.transitionOptions, errorBuilder.getPriority(), errorOverrideWidth, errorOverrideHeight, errorBuilder, callbackExecutor); // 给协调者设置主要、失败之后的请求。 errorRequestCoordinator.setRequests(mainRequest, errorRequest); return errorRequestCoordinator; } /** * 递归的调用构建缩略图请求,之所以递归?缩略图之下可能还有缩略图 */ private Request buildThumbnailRequestRecursive( Object requestLock, Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) { // 如果设置了缩略图的请求 if (thumbnailBuilder != null) { // Recursive case: contains a potentially recursive thumbnail request builder. if (isThumbnailBuilt) { throw new IllegalStateException( "You cannot use a request as both the main request and a " + "thumbnail, consider using clone() on the request(s) passed to thumbnail()"); } // 索取缩略图请求的配置选项 TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions = thumbnailBuilder.transitionOptions; // Apply our transition by default to thumbnail requests but avoid overriding custom options // that may have been applied on the thumbnail request explicitly. if (thumbnailBuilder.isDefaultTransitionOptionsSet) { thumbTransitionOptions = transitionOptions; } Priority thumbPriority = thumbnailBuilder.isPrioritySet() ? thumbnailBuilder.getPriority() : getThumbnailPriority(priority); int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth(); int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight(); if (Util.isValidDimensions(overrideWidth, overrideHeight) && !thumbnailBuilder.isValidOverride()) { thumbOverrideWidth = requestOptions.getOverrideWidth(); thumbOverrideHeight = requestOptions.getOverrideHeight(); } ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(requestLock, parentCoordinator); // 构建正常的请求 Request fullRequest = obtainRequest( requestLock, target, targetListener, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); isThumbnailBuilt = true; // Recursively generate thumbnail requests. // 构建缩略图请求 Request thumbRequest = thumbnailBuilder.buildRequestRecursive( requestLock, target, targetListener, coordinator, thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight, thumbnailBuilder, callbackExecutor); isThumbnailBuilt = false; coordinator.setRequests(fullRequest, thumbRequest); return coordinator; } else if (thumbSizeMultiplier != null) { // 如果配置了使用缩略图的简化版 // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse. ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(requestLock, parentCoordinator); Request fullRequest = obtainRequest( requestLock, target, targetListener, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); BaseRequestOptions<?> thumbnailOptions = requestOptions.clone().sizeMultiplier(thumbSizeMultiplier); Request thumbnailRequest = obtainRequest( requestLock, target, targetListener, thumbnailOptions, coordinator, transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight, callbackExecutor); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { // 不使用缩略图,使用默认的 SingleRequest // Base case: no thumbnail. return obtainRequest( requestLock, target, targetListener, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor); } }
-
开始进入请求的执行流程
弄清了请求的构建流程后,我们回来之前的请求执行流程。
// 执行请求 requestManager.track(target, request); // ReauestManager.java 这里调用 RequestTracker 去调用 Request 的 begin 方法。 synchronized void track(@NonNull Target<?> target, @NonNull Request request) { targetTracker.track(target); requestTracker.runRequest(request); } // RequestTracker.java /** Starts tracking the given request. */ public void runRequest(@NonNull Request request) { requests.add(request); if (!isPaused) { // 开始请求,Request 是个接口,常用的实现类——.java request.begin(); } else { request.clear(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Paused, delaying request"); } pendingRequests.add(request); } }
-
Request.begin() 方法
@Override public void begin() { synchronized (requestLock) { assertNotCallingCallbacks(); stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); // 这里的 model 就是在构建 RequestBulder 是传入了,最终加载的数据要转换的类型 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; } // 确保一个任务不多次同时执行 if (status == Status.RUNNING) { throw new IllegalArgumentException("Cannot restart a running request"); } // If we're restarted after we're complete (usually via something like a notifyDataSetChanged // that starts an identical request into the same Target or View), we can simply use the // resource and size we retrieved the last time around and skip obtaining a new size, starting // a new load etc. This does mean that users who want to restart a load because they expect // that the view size has changed will need to explicitly clear the View or Target before // starting the new load. if (status == Status.COMPLETE) { onResourceReady(resource, DataSource.MEMORY_CACHE); return; } // Restarts for requests that are neither complete nor running can be treated as new requests // and can run again from the beginning. 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 (IS_VERBOSE_LOGGABLE) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } } }
-
真正的开始加载数据
public void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); synchronized (requestLock) { if (IS_VERBOSE_LOGGABLE) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } // 将请求的状态改为运行中 status = Status.RUNNING; // 获取尺寸,及缩略的乘数 float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); if (IS_VERBOSE_LOGGABLE) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } // 开始加载 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, callbackExecutor); // This is a hack that's only useful for testing right now where loads complete synchronously // even though under any executor running on any thread but the main thread, the load would // have completed asynchronously. if (status != Status.RUNNING) { loadStatus = null; } if (IS_VERBOSE_LOGGABLE) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } } }
之后转到 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, Executor callbackExecutor) { long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0; // 这个 key 用于缓存 EngineKey key = keyFactory.buildKey( model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> memoryResource; synchronized (this) { // 从内存中加载数据 memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); // 内存中没有,开始一个新的数据加载 if (memoryResource == null) { return waitForExistingOrStartNewJob( glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime); } } // Avoid calling back while holding the engine lock, doing so makes it easier for callers to // deadlock. // 如果从内存中成功加载了缓存数据 cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE); return null; }
后续接着看
waitForExistingOrStartNewJob
private <R> LoadStatus waitForExistingOrStartNewJob( 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, Executor callbackExecutor, EngineKey key, long startTime) { EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb, callbackExecutor); 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, callbackExecutor); // 开始执行作业,丢给线程池执行,因此直接看任务中的 run() 方法,后续的操作是在子线程中进行的,这点要注意。 engineJob.start(decodeJob); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }
-
DecodeJob.run() 方法
方法很简单,直接转调用了
runWrapped(
) 方法。@Override public void run() { // This should be much more fine grained, but since Java's thread pool implementation silently // swallows all otherwise fatal exceptions, this will at least make it obvious to developers // that something is failing. GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model); // Methods in the try statement can invalidate currentFetcher, so set a local variable here to // ensure that the fetcher is cleaned up either way. DataFetcher<?> localFetcher = currentFetcher; try { if (isCancelled) { notifyFailed(); return; } // 从这一步开始要体会强大的缓存机制了 runWrapped(); } catch (CallbackException e) { // If a callback not controlled by Glide throws an exception, we should avoid the Glide // specific debug logic below. throw e; } catch (Throwable t) { // Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our // usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We // are however ensuring that our callbacks are always notified when a load fails. Without this // notification, uncaught throwables never notify the corresponding callbacks, which can cause // loads to silently hang forever, a case that's especially bad for users using Futures on // background threads. if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d( TAG, "DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage, t); } // When we're encoding we've already notified our callback and it isn't safe to do so again. if (stage != Stage.ENCODE) { throwables.add(t); notifyFailed(); } if (!isCancelled) { throw t; } throw t; } finally { // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call // close in all cases anyway. if (localFetcher != null) { localFetcher.cleanup(); } GlideTrace.endSection(); } } private void runWrapped() { switch (runReason) { case INITIALIZE: // 第一次加载(注意不代表是资源是第一次加载),需要判断从哪里进行数据加载 // 获取加载该资源的方式,从内存(分为:已解密的数据和未解码的数据)?从磁盘 // 注意点 1 stage = getNextStage(Stage.INITIALIZE); // 获取对应加载资源方式的模型 // 注意点 2 currentGenerator = getNextGenerator(); // 运行加载模型,加载数据 runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: // 切换回线程之后,开始进行数据转码 decodeFromRetrievedData(); break; default: throw new IllegalStateException("Unrecognized run reason: " + runReason); } }
下分面别看注意点 1 和注意点 2。
注意点一
不要在相信所谓的三级缓存。
private Stage getNextStage(Stage current) { //PS: 这里就是我们在使用时设置缓存策略的时候最终的值 switch (current) { case INITIALIZE: // 通过对缓存模型的判断,判断是否从缓存的数据中解码,缓存的就直接返回Stage.RESOURCE_CACHE 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); } }
注意点二
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); } }
-
strtNext() 使用数据加载模型进行数据加载
在数据模型生成以后接着调用
runGenerators
方法。private void runGenerators() { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false; // 看条件判断,具体代码看对应的数据加载模型实现类的 startNext 实现,startNext 中进行了数据加载。 while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { // 这一步是合用意?注意点,先分析完 startNext stage = getNextStage(stage); currentGenerator = getNextGenerator(); // if (stage == Stage.SOURCE) { reschedule(); return; } } // We've run out of stages and generators, give up. if ((stage == Stage.FINISHED || isCancelled) && !isStarted) { notifyFailed(); } // Otherwise a generator started a new load and we expect to be called back in // onDataFetcherReady. }
接着看
startNext()
方法。@Override 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; // 这一步有点看不懂,为什么这里要使用一个循环呢? // 解答:为了支持 Uri、Url、文件路径等方式加载数据,Glide 提供了多个数据加载模型 while (!started && hasNextModelLoader()) { // 这步对于 LoadData 即代码一种加载模型,和 DecodeFetcher 是配套的 ,这里就体现了官方使用文档的重要性! loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; // 最终的调用 Fetcher 开始加载数据,Url 默认使用的是:HttpUrlFetcher loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }
关于
ModuleLoader
的官方文档链接:[官方文档]( // 这步对于 LoadData 即代码一种加载模型,和 DecodeFetcher 是配套的 ,这里就体现了官方使用文档的重要性!链接:muyangmin.github.io/glide-docs-…) -
HttpUrlFetcher.loadData()
最后的连接数据源加载数据的代码比较简单了,官方默认使用 HttpUrlConnection 进行网络连接的请求。我们在使用的时候经常的会使用 OkHttp 进行替换,搜易上一步的
ModuleLoader
也是Glide
的设计巧妙点。@Override public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { long startTime = LogTime.getLogTime(); try { // 连接网络加载数据 InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); // 获取到数据之后调用回调方法 -》在 SourceGenerator.java 中 // 重点,这是重点,现在要准备进行数据解码了 callback.onDataReady(result); } catch (IOException e) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Failed to load data for url", e); } callback.onLoadFailed(e); } finally { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)); } } } private InputStream loadDataWithRedirects( URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException { if (redirects >= MAXIMUM_REDIRECTS) { throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!"); } else { // Comparing the URLs using .equals performs additional network I/O and is generally broken. // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html. try { if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) { throw new HttpException("In re-direct loop"); } } catch (URISyntaxException e) { // Do nothing, this is best effort. } } urlConnection = connectionFactory.build(url); for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(timeout); urlConnection.setReadTimeout(timeout); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Stop the urlConnection instance of HttpUrlConnection from following redirects so that // redirects will be handled by recursive calls to this method, loadDataWithRedirects. urlConnection.setInstanceFollowRedirects(false); // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352. stream = urlConnection.getInputStream(); if (isCancelled) { return null; } final int statusCode = urlConnection.getResponseCode(); if (isHttpOk(statusCode)) { return getStreamForSuccessfulRequest(urlConnection); } else if (isHttpRedirect(statusCode)) { String redirectUrlString = urlConnection.getHeaderField("Location"); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url"); } URL redirectUrl = new URL(url, redirectUrlString); // Closing the stream specifically is required to avoid leaking ResponseBodys in addition // to disconnecting the url connection below. See #2352. cleanup(); return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers); } else if (statusCode == INVALID_STATUS_CODE) { throw new HttpException(statusCode); } else { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } }
-
数据流获取成功,进行下一步的数据解码。
解码这一块也是
Glide
一个复杂的地方,因为官方在文档中有这样的表述:Glide 充分考虑了Android图片加载性能的两个关键方面:
- 图片解码速度
- 解码图片带来的资源压力
/** * 数据加载成功后调用 * @param data 加载成功的数据 */ @Override public void onDataReady(Object data) { // 获取磁盘缓存策略 DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); // 判断是否使用缓存策略,最终调用的两个回调都在 DecodeJob.java 中 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 回调是在构建此加载模型的时候传入的,因此后续的看 DecodeJob.java 中的两个方法。 cb.reschedule(); } else { // 不使用缓存 cb.onDataFetcherReady( loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }
@Override public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); } @Override public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this.currentSourceKey = sourceKey; this.currentData = data; this.currentFetcher = fetcher; this.currentDataSource = dataSource; this.currentAttemptingKey = attemptedKey; // 因为网络请求基本是在子线程中加载的,因此需要切换回 Gilde 所在的线程即主线程 if (Thread.currentThread() != currentThread) { // 看好这一步,这里又回到了 DecodeJob 的 run 方法中,注意 runReason 这个变量。 runReason = RunReason.DECODE_DATA; callback.reschedule(this); } else { GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); try { // 将网络请求得到的流数据装换成最终需要的数据,后面也是调用这个方法 decodeFromRetrievedData(); } finally { GlideTrace.endSection(); } } }
这里我们先不着急看
decodeFromRetrievedData()
这个方法,先看callback.reschedule()
@Override public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); } @Override public void onDataFetcherReady( Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { this.currentSourceKey = sourceKey; this.currentData = data; this.currentFetcher = fetcher; this.currentDataSource = dataSource; this.currentAttemptingKey = attemptedKey; // 判断当前线程是否运行初始工作的线程,如果不是则切换回去。 if (Thread.currentThread() != currentThread) { // 看好这一步,这里又回到了 DecodeJob 的 run 方法中,注意 runReason 这个变量。 runReason = RunReason.DECODE_DATA; // 这里的 callback 是在 DecodeJob 初始化得时候传入,实际上是 EngineJob.java ,后续看这个类中的回调。 callback.reschedule(this); } else { GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); try { // 将网络请求得到的流数据装换成最终需要的数据,后面也是调用这个方法 decodeFromRetrievedData(); } finally { GlideTrace.endSection(); } } }
因为这一步仅仅只是做切换线程,所以兜兜转转又回到了
DecodeJob.java
中,然后在 DecodeJob 的 run 方法中会去diaoy9ngdecodeFromRetrievedData()
。public void reschedule(DecodeJob<?> job) { // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself // up. // 切换线程,后续的动作继续看 DecodeJob 中的 run 方法。 getActiveSourceExecutor().execute(job); }
-
decodeFromRetrievedData() 开始数据的解码
经过几个转调用,最终会调用
decodeFromFetcher
,这里通过decodeHelper
获取了LoadPath
,LoadPath
负责对数据进行解码。private void decodeFromRetrievedData() { if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey( "Retrieved data", startFetchTime, "data: " + currentData + ", cache key: " + currentSourceKey + ", fetcher: " + currentFetcher); } Resource<R> resource = null; try { // 这里进行转码 resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); throwables.add(e); } if (resource != null) { // 解码完成,准备进行设置 notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
这里我们先看资源的转码过程,这里 LoadPath 是一个接口,其有多个实现类,一看我们就能知晓他们各自的作用了。
```java
// 这里我们先看资源的转码过程
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
// 获取 LoadPath
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
// 获取 LoadPath 的过程
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
// 通过 glidecontext 去获取对应的解码处理,transcodeClass 就是在构建 RequestBuilder 调用的 as 系列方法传入的 Drawable.class\Bitmap.class
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
// 将任务丢给 LoadPath
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
// 最后通过 LoadPath 进行最后的解码
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
// 用循环的原因,猜测是因为支持多种转码方式,但是一种成功即可.
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
// 又丢给 DecodePath 处理
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
```
- notifyEncodeAndRelease 开始准备设置和进行缓存
```java
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
// 通知解码完成
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
// 这个 callback 是 EngineJob,后面继续看 EngineJob
callback.onResourceReady(resource, dataSource);
}
```
-
绕来绕去的回调
@Override public void onResourceReady(Resource<R> resource, DataSource dataSource) { synchronized (this) { this.resource = resource; this.dataSource = dataSource; } // 这个后面又搞不懂了,应该要使用 Hnadler 了吧??? // 这里版本不同diamante是有个变动的,4.8 的这里直接调用了 Handle notifyCallbacksOfResult(); }
void notifyCallbacksOfResult() { ResourceCallbacksAndExecutors copy; Key localKey; EngineResource<?> localResource; synchronized (this) { stateVerifier.throwIfRecycled(); if (isCancelled) { // TODO: Seems like we might as well put this in the memory cache instead of just recycling // it since we've gotten this far... resource.recycle(); release(); return; } else if (cbs.isEmpty()) { throw new IllegalStateException("Received a resource without any callbacks to notify"); } else if (hasResource) { throw new IllegalStateException("Already have resource"); } engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener); hasResource = true; copy = cbs.copy(); incrementPendingCallbacks(copy.size() + 1); localKey = key; localResource = engineResource; } engineJobListener.onEngineJobComplete(this, localKey, localResource); // 这里执行了 ResourceCallback,先看这里 Call 是在那里添加的?addCallback() 是在该方法中添加 for (final ResourceCallbackAndExecutor entry : copy) { // 这里就是用主 Hanlder 执行了一个任务,这里的 cb 是 SingleRequest 在调用 load 方法是时候传入的,SingleRequest 实现了该接口(请求方式不是同会使用不同的 Request) entry.executor.execute(new CallResourceReady(entry.cb)); } decrementPendingCallbacks(); }
-
切换回主线程中设置数据
private class CallResourceReady implements Runnable { private final ResourceCallback cb; CallResourceReady(ResourceCallback cb) { this.cb = cb; } @Override public void run() { // Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock // (b/136032534). // 这是是在 Handler 中执行的方法 synchronized (cb.getLock()) { synchronized (EngineJob.this) { if (cbs.contains(cb)) { // Acquire for this particular callback. engineResource.acquire(); // 在主线程中调用,通知数据准备好了!记住 cb 是对应请求的 Request!!! callCallbackOnResourceReady(cb); removeCallback(cb); } decrementPendingCallbacks(); } } } } // SingleRequest.java public void onResourceReady(Resource<?> resource, DataSource dataSource) { // 这个方法是在主线程中执行的 stateVerifier.throwIfRecycled(); Resource<?> toRelease = null; try { synchronized (requestLock) { loadStatus = null; if (resource == null) { GlideException exception = new GlideException( "Expected to receive a Resource<R> with an " + "object of " + transcodeClass + " inside, but instead got null."); onLoadFailed(exception); return; } Object received = resource.get(); if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) { toRelease = resource; this.resource = null; onLoadFailed(exception); return; } if (!canSetResource()) { toRelease = resource; this.resource = null; // We can't put the status to complete before asking canSetResource(). status = Status.COMPLETE; return; } // 转调用 onResourceReady((Resource<R>) resource, (R) received, dataSource); } } finally { if (toRelease != null) { engine.release(toRelease); } } } private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) { // We must call isFirstReadyResource before setting status. boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; isCallingCallbacks = true; try { boolean anyListenerHandledUpdatingTarget = false; if (requestListeners != null) { for (RequestListener<R> listener : requestListeners) { anyListenerHandledUpdatingTarget |= listener.onResourceReady(result, model, target, dataSource, isFirstResource); } } anyListenerHandledUpdatingTarget |= targetListener != null && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource); if (!anyListenerHandledUpdatingTarget) { Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource); // 这里是重点!!!!!!调用目标方法为目标设置资源!!! target.onResourceReady(result, animation); } } finally { isCallingCallbacks = false; } notifyLoadSuccess(); }
-
目标 Target 设置资源
//ImageViewTraget.java @Override public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) { if (transition == null || !transition.transition(resource, this)) { // 设置数据 setResourceInternal(resource); } else { // 显示动画 maybeUpdateAnimatable(resource); } } private void setResourceInternal(@Nullable Z resource) { // Order matters here. Set the resource first to make sure that the Drawable has a valid and // non-null Callback before starting it. // 发现这里是一个抽象方法 setResource(resource); maybeUpdateAnimatable(resource); }
ImageViewTarget
是一个抽象类,其子类有如下几个,这里我们选择BitmapImageViewTarget
来看。
-
最终设置
见到了我们熟悉的代码了。
@Override protected void setResource(Bitmap resource) { view.setImageBitmap(resource); }
总结
Glide
的源码还是蛮复杂的,一路梳理下来对 Glide 的设计也是有了一个大致的认识。
首先 Glide 具有非常强大的灵活性,这一点可以从如下几点体现:
1. AppModule 和 LibraryModule 的设计,通多定义 AppModule 我们可以替换图片加载中的许多重要的流程,也可定义统一的加载图片的配置选项。
2. Target 的设计,让图片的加载不仅仅只限于 ImageView 的使用,还能用于自定义 View 和纯下载选项。
3. 缩略图加载设计,这一步使用递归的思想使得可以构建几乎无限极的缩略图加载,从而达到流程的图片显示。
4. 对于多种资源加载方式的支持,不同的加载请求,使用不同的 Request 且 Request 亦可以自行扩展。
5. 数据解码的实现使用了 LoadPath ,不同的数据对应不同对象,这个亦可以自行扩展,一支持更多的图片格式。
总体来说就是大体的流程Glide给我们一定定义好了,流程中各个关键的实现也充分给予了支持并且还允许自定义去控制各关键流程。面向接口编程的思想使用的淋漓尽致!!
同时,对于源码的阅读也是有了自己的理解,把握大体,不细究细节,需要时再来研究。阅读好开源项目时要有三次梳理,一次是配合官方使用文档快速的对整个流程进行数据,找出框架设计的模式,二次是对使用时的一些重点模块进行梳理,弄清各模块之间的配合,三次是在写源码阅读文章的时候,对之前的梳理进行查疑补漏,完成源码文章的写作!