前言
最近在组件化
开发中准备封装一个图片加载库
,于是乎就有了这篇文章
本篇文章对Glide
源码过程做了一个具体的解说,也是为了记载下自http://www.baidu.com己对Glide
的了解,今后忘掉还能够从这儿查找。
这儿我有几点主张:
- 看源码前先问下自己: 你为什么去看源码,是为了面试?学习?或许和我相同为了封装一个自己的图片加载库? 假如是为了面试,主张先列出几种同类型的开源库,比如咱们的图片加载库有架构师工资Picasso,Fresco和Glide,首先你要知道他们的根本运用办法,能说出他们的优缺点,并从中挑选https认证一个类库去深入了解,由于面试官很大或许会让你自接口crc错误计数己规划一套类似的开源库,那这个时分你有对某个类库做过深架构师入了解,会起到事半功倍的效果初始化,就算不能接口卡完整规划出来,也https安全问题能够说出一些根本原理和大致的架构。
个人对看源码的思路:
先看全体结构
:然后找到一个切入点
,深入结构内部去学习。学习中心记得依据结构进行分阶段总结
,这样才能不会深陷代码泥潭。好了下面咱们开端吧。http协议。
全体结构图

源码整理
咱们将源码分为三个部分来解说:
with
load
和into
这个三个部分便是咱们看源码的切入点:
过程1:wit初始化h:
Glide.java
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
checkAndInitializeGlide(context);
}
private static void checkAndInitializeGlide(@NonNull Context context) {
initializeGlide(context);
}
private static void initializeGlide(@NonNull Context context) {
initializeGlide(context, new GlideBuilder());
}
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
//1.获取注解自动生成的AppGlideModule
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
//这儿解析manifest中的,metedata字段,并增加到list的格局的manifestModules中
manifestModules = new ManifestParser(applicationContext).parse();
}
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
//给builder设置一个RequestManagerFactory,用于创立RequestManager
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
//创立Glide目标 --关注点1--
Glide glide = builder.build(applicationContext);
//这儿给manifestModules中设置的GlideModule注册到运用的生命周期中
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
//将运用的applicationContext和build创立的glide注册到:
//之前运用注解@GlideModule创立的GlideApp中,能够让GlideApp在退出时,能够对glide或许app做一些处理
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
//这儿将glide注册到运用的生命周期中:
//glide完成了ComponentCallbacks接口,这个接口有两个办法onConfigurationChanged和onLowMemory,
//在运用回调这两个接口的时分,glide能够感知到,能够对缓存或许图片做一些处理等
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
--关注初始化英文点1--
Glide build(@NonNull Context context) { //创立SourceExecutor线程池 if (sourceExecutor == null) { sourceExecutor = GlideExecutor.newSourceExecutor(); } //创立diskCacheExecutor磁盘缓存线程池 if (diskCacheExecutor == null) { diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); } //创立animationExecutor动画线程池 if (animationExecutor == null) { animationExecutor = GlideExecutor.newAnimationExecutor(); } //创立内存大小测量器,后期会依据这个测量器测出的内存状况,对缓存进行操作 if (memorySizeCalculator == null) { memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); } //这个类里边对Activity或许Fragment的生命周期做了监听,在Activity或许Fragment处于活跃状况时去监听网络连接状况,在监听中做一些重启等操作 if (connectivityMonitorFactory == null) { connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); } //创立一个BitMap的缓存池,内部FIFO if (bitmapPool == null) { int size = memorySizeCalculator.getBitmapPoolSize(); if (size > 0) { bitmapPool = new LruBitmapPool(size); } else { bitmapPool = new BitmapPoolAdapter(); } } //创立一个缓存池列表 if (arrayPool == null) { arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); } //创立一个Resource缓存池 if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } //创立磁盘缓存池 if (diskCacheFactory == null) { diskCacheFactory = new InternalCacheDiskCacheFactory(context); } //将上面创立的一切目标封装到一个Engine目标中,今后一切需求的缓冲池或许线程池都在这儿面获取即可 if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), animationExecutor, isActiveResourceRetentionAllowed); } //这儿是回调列表,用户设置的Listener有或许会有多个 if (defaultRequestListeners == null) { defaultRequestListeners = Collections.emptyList(); } else { defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); } //这儿是设置一些Glide的实验性数据,这儿不必太过关注 GlideExperiments experiments = glideExperimentsBuilder.build(); RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory, experiments); //最终将上面创立的一切目标都保存到一个Glide目标中,并回来 return new Glide( context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel, defaultRequestOptionsFactory, defaultTransitionOptions, defaultRequestListeners, experiments); }
看Glide构造办法:
Glide(...){ 1.创立了一个Registry,这个办法首要是用来注册modelClass和dataClass和factory对应,由此能够通过modelClass找到对应的dataClass和factory registry = new Registry(); registry.register(new DefaultImageHeaderParser()); registry .append(ByteBuffer.class, new ByteBufferEncoder()) .append(InputStream.class, new StreamEncoder(arrayPool)) ... //这儿是一个比较重要的,标注TODO1。后面调用DataFetch会用到这个注册的factory .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory()) .. //这儿省掉了很多append调用: }
回到调用 “关注点1
”的代码处
initia初始化磁盘lizeGlide
办法解说完后初始化电脑时出现问题未进行更改,咱们回调最开端的with
调用办法处:
public static RequestManager with(@NonNull Context context) { return getRetriever(context).get(context); } 这儿有个get办法:咱们进入看看 public RequestManager get(@NonNull Context context) { //传入的context不能为null 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()); } } return getApplicationManager(context); }
这儿咱们看到:
-
1.假如
woth
办法在子线程
运行,则会走到getApplicationManager(context
),这儿会创立一个大局的http 500Glide
,不会随着控件生命周期
毁掉 -
2.假如是
主线程
:-
2.1:context是
Fhttps协议ragmentActivit接口卡y
,回来一个Fragment的生命周期的Glide RequestManager -
2.2:context接口自动化是
Act架构图模板ivity
,回来一个Activity的生命周期的Glide RequestManager -
2.3:context是
ContextWrapper
,回来一个Conte接口测试用例设计xtWrapper的生命周期的Glide RequestManager
-
2.1:context是
- 3.其他状况就回来运用层的RequestManager,
注意
:
所以在子线程中创立的RequestManager都是大局运用的Request接口Manager,只能感知运用状况,无法感知控件状况
总结with办法:
效果:初始化
glide
:创立架构工程师了多种线程池
,多种缓存池
,将glide注册到运用控件的生命周期
中,能够感知运用的内存状况以及界面配置等信息回来值:
RequestManager
过程2:load
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
- 看到
asDrawable
其实便是创立了一个RequestBuilder
来看Re初始化电脑时出现问题未进行更改questBuilder
的load
办法:
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
//这个判别默以为false
if (isAutoCloneEnabled()) {
return clone().loadGeneric(model);
}
this.model = model;
isModelSet = true;
return selfOrThrowIfLocked();
}
load
办法很简单:
便是创立了一个
RequestBuilder
,并设置了恳求的url
信息
过程3:into
这儿接口文档才是glide的重https和http的区别点
前面两个过程都是初始化操作 咱们将into办法分为三个阶段:
deco初始化磁盘deJob前
decodeJ架构师工资ob开端
decodeJ初始化失败是怎么解决ob结束
阶段1
:decodeJob前
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { //先断语是在主线程上,假如是子线程则抛反常退出, //这就话能够看出glide能够在子线程初始化,可是into操作必定需求在主线程履行 Util.assertMainThread(); //传入的view不能为null,否则会抛反常 Preconditions.checkNotNull(view); BaseRequestOptions<?> requestOptions = this; //这儿是设置view的scaleType,glide默许提供了四种scaleType if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { // Clone in this method so that if we use this RequestBuilder to load into a View and then // into a different target, we don't retain the transformation applied based on the previous // View's scale type. switch (view.getScaleType()) { case CENTER_CROP: requestOptions = requestOptions.clone().optionalCenterCrop(); break; case CENTER_INSIDE: requestOptions = requestOptions.clone().optionalCenterInside(); break; case FIT_CENTER: case FIT_START: case FIT_END: //默许是运用这个状况设置图片 requestOptions = requestOptions.clone().optionalFitCenter(); break; case FIT_XY: requestOptions = requestOptions.clone().optionalCenterInside(); break; case CENTER: case MATRIX: default: // Do nothing. } } return into( glideContext.buildImageViewTarget(view, transcodeClass), //关注点2 /*targetListener=*/ null, requestOptions, Executors.mainThreadExecutor()); }
来看关注点2
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
能够看到
- 传入的是
Bi架构工程师tmap
.class.则创立BitmapImageViewTar架构师get
- 假如是
Drawable
.class,则创立DrawableImageViewTarget
并将传入的ImageView
包裹进
过程2 load办法中有了解过,load办法默许调用了asDrawable,所以https认证回来的http 302是Drawable类型的数据, 这儿假如需求拿到的是B初始化电脑的后果itmap的类型图片数据,则需求调用asBitm接口测试用例设计ap,越接口英文过默许asDrawable办法的履行
持续回到关注点2调用
的地方:调用了一个内部的into
办法
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
...
//关注点3
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;
}
来看关注点3
:
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
/*requestLock=*/ new Object(),
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
持续看buildRequestRecursive 为了不让咱们看的头晕,非要害代码我这边省掉了:
private Request buildRequestRecursive(...) { ... //这儿创立一个缩略图的恳求:关注点4 Request mainRequest = buildThumbnailRequestRecursive( requestLock, target, targetListener, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, requestOptions, callbackExecutor); ... //这儿创立一个errorRequest恳求 Request errorRequest = errorBuilder.buildRequestRecursive( requestLock, target, targetListener, errorRequestCoordinator, errorBuilder.transitionOptions, errorBuilder.getPriority(), errorOverrideWidth, errorOverrideHeight, errorBuilder, callbackExecutor); errorRequestCoordinator.setRequests(mainRequest, errorRequest); return errorRequestCoordinator; }
进入关注点4
private Request buildThumbnailRequestRecursive(...)
{
...
if (thumbnailBuilder != null) {
Request fullRequest = obtainRequest(...)
...
Request thumbRequest = thumbnailBuilder.buildRequestRecursive(...)
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
}else if(thumbSizeMultiplier != null){
Request fullRequest = obtainRequest(...)
Request thumbnailRequest = obtainRequest()
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
}else{
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
进入obtainRequest看看:
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
}
- 这儿创立了一个
SingleRequest
的恳求类,先记住这儿
总结下关注点3:buildRequest
假如有要求加载缩略图恳求的,会将缩略图恳求和图片实在恳求放到一起。 假如没有缩略图恳求,则创立一个SingleRequest的恳接口测试用例设计求回来给调用测
咱们回调关注点3
被调用处:这儿我将前面代码再次拷贝一次咱们就不必再次向前翻找了。
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
...
//关注点3
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;
}
//恳求前先调用clear一下这个target
requestManager.clear(target);
target.setRequest(request);
//这儿是实在恳求进口 咱们进去看看
requestManager.track(target, request);
return target;
}
track办法:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
//这儿将target放入一个targets集合中
targetTracker.track(target);
//恳求进口
requestTracker.runRequest(request);
}
runRequest办法:
public void runRequest(@NonNull Request request) {
requests.add(request);
//假如不是暂停状况,则调用begin发动恳求,假如是,则将request放入pendingRequests,推迟发动,
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
进入begin
:
在解说SingleRequest
前,咱们下来了解下一个Request
的几种状况:
private enum Status { /** Created but not yet running. */ PENDING, /** In the process of fetching media. */ RUNNING, /** Waiting for a callback given to the Target to be called to determine target dimensions. */ WAITING_FOR_SIZE, /** Finished loading media successfully. */ COMPLETE, /** Failed to load media, may be restarted. */ FAILED, /** Cleared by the user with a placeholder set, may be restarted. */ CLEARED, }
这个Status架构图
是SingleRequest
的一个内部枚举类:
-
PENDIN架构图怎么画G
:挂起状况,还未运行,待运行,前面咱们说过假如R初始化电脑的后果equest处于isPaused状况,会增加进入初始化sdk什么意思pendingRequests的list中,这个里边的Request便是处于PENDING状况 -
RUNNING
:这个状况的架构Request正在去服务器拿数据阶段httpwatch -
WAITING_F初始化电脑时出现问题OR_SIZE
:等初始化电脑的后果待获取ImageVie架构w的一个尺寸阶段 -
COMPLETE
:初始化磁盘成功拿到恳求数据http://192.168.1.1登录 -
FAILED
:没有获取到恳求数据,在必定状况下会重启,例如:在网络状况杰出的时分,Glide会自动重启恳求,去获取数据,由于Glide注册了网络状况的监听 -
CLEARED
:被用户整理状况,运用一个placeholder替代数据元,在必定状况下会重启Request
好了,有了上面的根底咱们再来看下面代码:
这个request
是在关注点3
处创立的初始化SingleRequest
public void begin() { synchronized (requestLock) { ... //恳求处于RUNNING状况,这个反常会被丢弃 if (status == Status.RUNNING) { throw new IllegalArgumentException("Cannot restart a running request"); } ... //恳求处于COMPLETE,则回调onResourceReady,让运用去内存中获取,这儿的DataSource.MEMORY_CACHE:标志数据元在内存缓存中 if (status == Status.COMPLETE) { onResourceReady( resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false); return; } ... 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()); } } }
从begin
办法中,看到对恳求的状况做了判别,只有是未处理的恳求才会去恳求数据,其他状况依据具体状况处理
咱们进入onSizeReady
看看:
public void onSizeReady(int width, int height) {
...
loadStatus = engine.load(...)
...
}
public <R> LoadStatus load(...){
EngineResource<?> memoryResource;
synchronized (this) {
//去缓存池中获取数据 ,这儿面内部先去活动的缓存中activeResources获取图片数据,假如没有获取到就去缓存池cache中获取,都是通过对应的key获取
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
//没有取到缓存数据,则进入waitForExistingOrStartNewJob,看名字应该是去恳求数据的办法,咱们进入这儿面看看
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, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
private <R> LoadStatus waitForExistingOrStartNewJob(
...
//这儿创立一个EngineJob
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//这儿创立一个decodeJob
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
//将engineJob放入jobs中
jobs.put(key, engineJob);
//给engineJob增加回谐和线程池履行器
engineJob.addCallback(cb, callbackExecutor);
//发动engineJob,并传入decodeJob
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob)
}
进入engineJob.start(decodeJob)
办法看看:
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
-
内部其http 302实便是运用线程池去恳求
decodeJob
:接口测试用例设计 -
履行架构师和程序员的区别
decodeJob
前,咱们先来整理下咱们阶接口类型段1剖析的流程
RequestBuilder.java into{ 1.对ScaleType的RequestOption做处理 2.调用内部into办法履行 into(参数1:DrawableImageViewTarget(view),参数2:requestOptions,参数3:主线程履行器); { 1.创立Request:buildRequest,回来值:SingleRequest目标 2.查看是否和前一个恳求是同一个,假如是,则直接运用前一个恳求发送begin办法,否就走第3步 3.铲除当时target恳求; 4.进入requestManager.track(target, request): track{ 1.调用requestTracker.runRequest(request): runRequest{ //假如不是暂停状况,则调用begin发动恳求,假如是,则将request放入pendingRequests,推迟发动, 1.request.begin():这个request是一个SingleRequest目标 begin{ 1.对恳求状况做判别 2.调用onSizeReady onSizeReady{ engine.load{ 1.去缓存中获取数据:loadFromMemory 2.1中没有获取到缓存数据,则调用waitForExistingOrStartNewJob waitForExistingOrStartNewJob{ 1.这儿面发动//发动engineJob,并传入decodeJob:engineJob.start(decodeJob) start{ 1.调用线程池发动decodeJob } } } } } } } } }
阶段2:decodeJob
履行阶段
在解说decodeJob
履行阶段前咱们先来了解下Job
的几种状况:
/** Why we're being executed again. */ private enum RunReason { /** The first time we've been submitted. */ INITIALIZE, /** We want to switch from the disk cache service to the source executor. */ SWITCH_TO_SOURCE_SERVICE, /** * We retrieved some data on a thread we don't own and want to switch back to our thread to * process the data. */ DECODE_DATA, }
这个RunR初始化eason
看httpwatch名字就知道:这儿是表明你发动这个Job是要做什么的,首要有三种状况:
-
INITIAL架构师和程序员的区别IZE
: 便是一个新的恳求有必要通过的第一步,这儿joHTTPSb首要使接口卡命是去缓存中取数据 -
SWITCH_TO_架构师和程序员的区别SOURCE_SERVICE
:httpwatch 便是咱们期望这个job完成从https和http的区别磁盘服务到资源获取阶段, 这个怎样了解呢,了解Ok初始化是什么意思Http源码都知道,OkHttp内部运用的是拦截器的形式对恳求做处理,每个恳求都需求通过拦截器顺次处理,最终得到恳求数据, 假如OkHTTPSHttp发现缓存拦截器中有数据,则回来,没有数据,则履行下一个拦截器。 这儿也是相同,每个job类似一个拦截器,每次都是优先去缓存中获取数据,也便是第一步的INITIALIZ接口测试用例设计E状况的job,假如没有数据才会去网络中恳https协议求, SWITCH_TO_SOU接口RCE_SERVICE就表明咱们缓存中没有获取到数据,需求发动下一个job去网络中拿数据。 -
DECODE_DATA
: 这个架构工程师很好http 404了解,便是在异步线程上获取到数据,期望跳转到咱们自己的线程上接口自动化去解码数据
这儿再初始化电脑的后果介绍一个内部枚举类:
/** Where we're trying to decode data from. */ private enum Stage { /** The initial stage. */ INITIALIZE, /** Decode from a cached resource. */ RESOURCE_CACHE, /** Decode from cached source data. */ DATA_CACHE, /** Decode from retrieved source. */ SOURCE, /** Encoding transformed resources after a successful load. */ ENCODE, /** No more viable stages. */ FINISHED, }
- 这个类标识咱们应该去哪个类中履行当时
Job
: 每个Job
都有对应的履行器,履行完后,调用startNext
履行下一个Job
,下一个job又是对应另外一个stage
的履行器
首要是在下面一个办法中运用:
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); } }
-
RESOURCE_CACHE
:对应ResourceCacheGener架构ator
履行器 -
DATA_CACHE
:对应DataCacheGenerator
履行器 -
SOURCE
:对应SourceGenerator
履行器
通过上面的剖析,咱们现已知道:
你能够把一个Job了解为一个拦截器,每次都会依据当时Job的接口文档状况是要不同的履行器去履行使命
-
使命总线
:
1.ResourceC初始化失败是怎么解决acheGenerator
:缓存中获取数据,这个数据是被修改正的降采样缓存
2.DataCacheGenerator
:也是缓存中获初始化是什么意思取数据,这个数据是没有被修改的网路恳求元数据
3.SourceGenerator
:这个履行器履行的使命才真正是用于网络恳求数据
是架构图模板不是和咱们的OkHttp
很像?
有了上面的根底咱们再来对decodeJob
源码进行剖析:
DecodeJob承初始化失败是怎么解决继了Runnable接口,咱们来看他的run办法
public void run() { ... runWrapped(); ... } private void runWrapped() { switch (runReason) { case INITIALIZE: //关注点1 stage = getNextStage(Stage.INITIALIZE); //关注点2 currentGenerator = getNextGenerator(); //关注点3 runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: decodeFromRetrievedData(); break; default: throw new IllegalStateException("Unrecognized run reason: " + runReason); } }
runWrapped
办法中对runReason
做一个判别:别架构图怎么制作离指向不同的恳求:
//关注点1 咱们看INITIALIZE,调用了getNextStage传入Stage.INITIALIZE
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: // 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); } }
这儿面是依据传入的diskC接口是什么achehttps和http的区别Strategy
磁盘缓存策略对应不同的状况
咱们架构师工资假设diskCacheStrategy
.decodeCachedResource
()回来都是true
则next
联系如下:
INITIALIZE->RESOURCE_CACHE->DATA_CACHE->SOURCE->FINISHED
回到前面ru初始化电脑时出现问题未进行更改nWra接口英文pped
办法:
//关注点2 getNextGenerator获取履行器前面现已讲过,咱们再发下对应联系:
-
RESOURCE_CACHE
:对应ResourceCacheGenerator
履行器 -
DATA_CACHE
:对应DataCacheGenerator
履行器 -
SOURChttp 500E
:对应SourceGenerator
履行器
//关注点3 调用runGener初始化电脑时出现问题未进行更改ators开端履行对应的job
private void runGenerators() { currentThread = Thread.currentThread(); startFetchTime = LogTime.getLogTime(); boolean isStarted = false; while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } }
能够看到这儿有个while循环便是依据当时stage状况循环履行不同的恳求:
按前面咱们的剖析
:
优先会履行:
ResourceCacheGenerator
,然后DataCacheGenerator
,最终SourceGenerator
篇幅问题:咱们这只具体剖析SourceGenerator的办法:其他两个履行器前面现已介绍过
SourceGenerator.java
public boolean startNext() {
...
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;
startNextLoad(loadData);
}
}
...
return started;
}
看startNextLoad办法:
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
这个fetcher = MultiFetcher
目标
进入M接口类型u接口crc错误计数ltiFetcher的loadData
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super Data> callback) { ... fetchers.get(currentIndex).loadData(priority, this); ... }
fetchers
是一个List
,内部存储的是一个HttpUr初始化电脑时出现问题未进行更改lFeacher
这儿传入了一个this作为callback回调,后面会用到:this = MultiFetcher
HttpUrlFeacher.java public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { ... try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { callback.onLoadFailed(e);//反常回调onLoadFailed接口给运用层 } finally { ... } } loadDataWithRedirects办法: private InputStream loadDataWithRedirects( URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException { ... //关注点1 urlConnection = buildAndConfigureConnection(url, headers); try { // 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(); } catch (IOException e) { throw new HttpException( "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e); } ... //这儿获取回来code final int statusCode = getHttpStatusCodeOrInvalid(urlConnection); if (isHttpOk(statusCode)) { //关注点2 return getStreamForSuccessfulRequest(urlConnection); } else if (isHttpRedirect(statusCode)) { String redirectUrlString = urlConnection.getHeaderField(REDIRECT_HEADER_FIELD); if (TextUtils.isEmpty(redirectUrlString)) { throw new HttpException("Received empty or null redirect url", statusCode); } URL redirectUrl; try { redirectUrl = new URL(url, redirectUrlString); } catch (MalformedURLException e) { throw new HttpException("Bad redirect url: " + redirectUrlString, statusCode, e); } // 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 { try { throw new HttpException(urlConnection.getResponseMessage(), statusCode); } catch (IOException e) { throw new HttpException("Failed to get a response message", statusCode, e); } } }
//关注点1
这儿首要是创立H初始化失败是怎么解决ttpUrlConnechttp 500tion
恳求架构师工资
private HttpURLConnection buildAndConfigureConnection(URL url, Map<String, String> headers) throws HttpException { HttpURLConnection urlConnection; try { urlConnection = connectionFactory.build(url); } catch (IOException e) { throw new HttpException("URL.openConnection threw", /*statusCode=*/ 0, e); } 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); return urlConnection; }
//关注点2
回来成功,则获取InputS接口英文tream
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection) throws HttpException { try { if (TextUtils.isEmpty(urlConnection.getContentEncoding())) { int contentLength = urlConnection.getContentLength(); stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength); } else { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding()); } stream = urlConnection.getInputStream(); } } catch (IOException e) { throw new HttpException( "Failed to obtain InputStream", getHttpStatusCodeOrInvalid(urlConnection), e); } return stream; } 持续回到HttpUrlFeacher的loadData办法 public void loadData( @NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { ... try { InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); } catch (IOException e) { callback.onLoadFailed(e);//反常回调onLoadFailed接口给运用层 } finally { ... } }
把回来的InputStream
通过callbachttps协议k
回调给上一级
这个callback 前面剖析过:callBack = MultiFetcher
回到Mul接口自动化tiFetcher的onDataReady
办法中:
public void onDataReady(@Nullable Data data) { if (data != null) { callback.onDataReady(data); } else { startNextOrFail(); } }
看到这儿又调用了一次callback.onDataReady(data);
这个callback = loadData传入下来的S架构图模板ourceGenerato接口卡r
中的startNextLoad
办法:
SourceGenerator.java
private void startNextLoad(final LoadData<?> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
回调这儿再次调用onDataReadyInter架构图模板nal
办法:
void onDataReadyInternal(LoadData<?> loadData, 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); } }
看cb.reschedule()办法:这个cb = Decod初始化英文eJob
DecodeJob.java public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); } 这个callback是EngineJob EngineJob.java 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. getActiveSourceExecutor().execute(job); }
到这儿又去履行了一次job,这个job 的 run架构Reason = RunReason.SWITCH_TO_SOURCE_SERVICE
前面第一次获取网络数据也调用过这个:
前面是为了获取缓存数据:这次是为了将数据写入缓存
最终将数据通过DecodeJob的onDataFetcher架构师工资Ready回调出去
这儿对阶段2做一个总结:
-
工作
:首要担任去缓存中取数据,假如没有取到接口类型就去网络服务器拉数据,获http://www.baidu.com取到数据后,将数据放入HTTP到缓存中。 运用的是Job形式,每个Job都有一个状况,每个状况关于一个履行器,类似OkHttp中的拦截器形式
阶段3
:解码数据
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; this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0); if (Thread.currentThread() != currentThread) { runReason = RunReason.DECODE_DATA; callback.reschedule(this); } else { GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData"); try { decodeFromRetrievedData(); } finally { GlideTrace.endSection(); } } }
- 这个办法中又调用了
deco架构图deFromRetrievedData
- 效果:解码得到的响应数据
private void decodeFromRetrievedData() { try { //这儿解码数据 关注点1 resource = decodeFromData(currentFetcher, currentData, currentDataSource); } catch (GlideException e) { e.setLoggingDetails(currentAttemptingKey, currentDataSource); throwables.add(e); } if (resource != null) { //通知运用 关注点2 notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey); } else { runGenerators(); } }
看 关注点1
:
decodeFromData
decodeFromFetcher(data, dataSource);
runLoadPath(data, dataSource, path);
path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
result = path.decode(rewinder, width, height, options, decodeCallback);
decodeResource(rewinder, width, height, options);
decodeResourceWithList(rewinder, width, height, options, exceptions);
result = decoder.decode(data, width, height, options);
downsampler.decode(source, width, height, options);
decode(...);
decodeFromWrappedStreams
Bitmap downsampled = decodeStream(imageReader, options, callbacks, bitmapPool);
result = imageReader.decodeBitmap(options);
BitmapFactory.decodeStream(stream(), /* outPadding= */ null, options);
能够看到恳求数据解码过程最终也是通过BitmapFactory.decodeStream
将数据转换为Bitmap
回来
可是这个过程中Glide对数据做了多重处理,包含裁剪,接口卡降采
样等操作
关注初始化游戏启动器失败点2:notifyEncode接口类型AndRelease
private void notifyEncodeAndRelease(
...核心代码
notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey);
}
private void notifyComplete(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
}
调用了callback.onResourceReady:
这个callback = EngineJob
EngineJob.java
public void onResourceReady(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
this.isLoadedFromAlternateCacheKey = isLoadedFromAlternateCacheKey;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
...
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
履行了CallResourceReady的run办法
run
callCallbackOnResourceReady(cb);
cb.onResourceReady(engineResource, dataSource, isLoadedFromAlternateCacheKey); cb = SingleRequest
onResourceReady
target.onResourceReady(result, animation); 这个target = BitmapImageViewTarget ,这儿调用其父类ImageViewTarget的onResourceReady办法
setResourceInternal(resource);
setResource(resource);履行BitmapImageViewTarget的setResource
view.setImageBitmap(resource);到这儿就履行结束了
- 总结阶段3:便是对网络数据依据需求进行降采样,并将数据设置到对应的target中
这儿对整个过程3:into
办法做一个总结吧:
阶段1
: 创立对应的网络恳求,对比当架构工程师时恳求和接口文档内存中前几个恳求是否共同,假如一向,运用前一个恳求进行网络架构师和程序员的区别恳求。 去缓存中获取对应的数据,假如没有获取到就去网https和http的区别络拉取初始化磁盘数据
阶段2
: 首要担任去缓存中取数据,假如没有取到就去网络http代理服务器拉数据,获取到数据后,将数据放入到缓存中。 运用的是Job形式,接口测试用例设计每个Job都有一个状况,每个状况关于一个履行器,类似O架构是什么意思kHttp中的拦截器形式 依据job状况的切换,履行各自的恳求
阶段3
:对数据进行解码,解码会进行依据需求进行降采样,并将获取的图片数据传递给对应的Ta初始化sdk什么意思rget
Glide缓存
这儿咱们对Glide三级缓存
做一个总结:由于一些面试还是经常会问到的
1.怎样缓存?
假如支持磁盘缓存,则将原Source
缓存到磁盘,在通过解码处理后,将解码后的数据缓存到activeResources
,activeResources
是一个弱引证的目标
,在内存回收的时分,会将数据放到cac初始化是什么意思he缓存下面
2.怎样运用缓存
假如直接内存缓存,则去activeResources
中取解码后的数据,假如activeResourhttps认证ces
中没有对应的缓存,则去cac架构工程师he
缓存中获取,如找到则直接回来cache数据,并https协议将缓存数接口自动化据写入activeResources中,删除cache中的缓存数据,
假如还没有,则在调用网络恳架构师求前,去磁盘中获取,磁盘中获取架构是什么意思的数据是图片原数据,需求通过解码处理才能被控件运用,磁盘假如还没有,才接口测试用例设计会去网络中恳求数据。
以上便是Glide对三级缓存的运用机制。
总结
最终用一幅图来整理下过程:来自 JsonChao
由于是旧版架构图怎么画本画的流程图,所以有些地方或许会不共同,可是大体流程是共同的,能够参阅图片再去剖析源码

评论(0)