Android Glide图片加载框架(二)源码解析之into()

文章目录

  • 一、前言
  • 二、源码解析
    • 1、into(ImageView)
    • 2、GlideContext.buildImageViewTarget()
    • 3、RequestBuilder.into(Target,RequestListener,RequestOptions);
    • 4、RequestBuilder.buildRequest()
    • 5、SingleRequest.obtain()
    • 6、isEquivalentTo()、isSkipMemoryCacheWithCompletePreviousRequest()
    • 7、RequestManager.track()
    • 8、SingleRequest.begin()
    • 9、ImageViewTarget.onLoadFailed()
    • 10、ImageViewTarget.onLoadStarted()、onResourceReady()
    • 11、SingleRequest.onSizeReady()
    • 12、Engine.load()
    • 13、DecodeJob.run()
    • 14、DecodeJob.runWrapped()
    • 15、DecodeJob.runGenerators()
    • 16、SourceGenerator.startNext()
    • 17、HttpUrlFetcher.loadData()
    • 18、HttpUrlFetcher.loadDataWithRedirects()


Android Glide图片加载框架系列文章

Android Glide图片加载框架(一)基本用法

Android Glide图片加载框架(二)源码解析之with()

Android Glide图片加载框架(二)源码解析之load()

Android Glide图片加载框架(二)源码解析之into()

Android Glide图片加载框架(三)缓存机制


一、前言


在源码解析系列文章中,上一篇我们分析了 load() 方法的用法及作用,主要涉及到 RequestManagerRequestBuilder ,获取了我们的资源参数和想要加载的结果类型,并已经持有了这些参数,接下来就是Glide的核心部分——加载图片,这一篇将开始分析Glide的 into() 方法,这一部分内容有点多,也是整个Glide图片加载流程中逻辑最复杂的地方,你准备好了吗?


二、源码解析


1、into(ImageView)


Glide中 into() 的使用,我们一般都是传递了一个 ImageView 这样子的图片加载控件,然后Glide就会帮我们把之前传进去的资源给绘制到ImageView上,表面轻描淡写,底下实则深似水。

上一篇我们讲了 load() 方法最终返回了 RequestBuilder对象,由于Glide的代码是链式调用,那么 into() 方法也在 RequestBuilder 类中,下面我们就看下RequestBuilder类的into()方法,代码如下:

public class RequestBuilder<TranscodeType> implements Cloneable,ModelTypes<RequestBuilder<TranscodeType>> {@NonNullpublic ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {Util.assertMainThread();Preconditions.checkNotNull(view);RequestOptions requestOptions = this.requestOptions;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),/*targetListener=*/ null,requestOptions);}
}

第一步: 判断传进来的 ImageView 是否为空;

第二步: 获取了一个requestOptions参数,是不是很眼熟,这就是我们在使用Glide时调用 apply() 方法传进来的 RequestOptions

第三步: 获取ImageView是否有设置了 ScaleType,如果有,就优先使用其ScaleType来作为最终展示的ScaleType;

第四步: 调用了另一个 into(Target, RequestListener, RequestOptions);,这里涉及到一个新的概念——Target。


2、GlideContext.buildImageViewTarget()


那么Target有什么作用呢?跟进去 buildImageViewTarget()

public class GlideContext extends ContextWrapper {@NonNullpublic <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {return imageViewTargetFactory.buildTarget(imageView, transcodeClass);}
}

这里其实又是调用了 ImageViewTargetFactory的buildTarget() 方法来生成对应的 ViewTarget ,我们继续跟进去,代码如下所示:

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)");}}
}

可以看到,在 buildTarget() 方法中会根据传入的 class 参数来构建不同的Target对象。那么这个class参数是哪来的呢?还记得上一篇讲解load()方法时 RequestBuilder 类中的 transcodeClass 变量吗,这个class参数其实就是transcodeClass变量,该值基本上只有两种情况,如果你在使用Glide加载图片的时候调用了 asBitmap() 方法,那么这里就会构建出 BitmapImageViewTarget 对象 ,否则的话构建的都是 DrawableImageViewTarget 对象 。

为何Glide要区分两种 BitmapDrawable 呢,因为ImageView加载这两个的api是不一样的,点进去可以看到它们的最明显区别:

在这里插入图片描述
在这里插入图片描述
所以,我们刚才的 buildImageViewTarget() 方法的作用就是根据传进来的 transcodeClass ,生成对应的 ImageViewTarget ,且可以很明显看出,这个ImageViewTarget是用来最终展示图片的,这正好对应了我们 transcodeClass 的作用。即通俗的讲,我们在外面调用的 asBitmapasDrawable,最终Glide会在这里根据不同的类型调用ImageView的 setImageBitmap 或者 setImageDrawable 进行展示


3、RequestBuilder.into(Target,RequestListener,RequestOptions);


解释完 Target 的作用,我们再回到刚才的 into(Target,RequestListener,RequestOptions); ,跟进去看到:

public class RequestBuilder<TranscodeType> implements Cloneable,ModelTypes<RequestBuilder<TranscodeType>> {private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,@NonNull RequestOptions options) {Util.assertMainThread();Preconditions.checkNotNull(target);if (!isModelSet) {throw new IllegalArgumentException("You must call #load() before calling #into()");}options = options.autoClone();Request request = buildRequest(target, targetListener, options);Request previous = target.getRequest();if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {request.recycle();// 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;}
}

第一步: 判断是否为主线程,毕竟Android不允许在子线程展示UI;

第二步: 判断 Target 是否为空,因为刚说了,Target是用来展示图片的,必须要有;

第三步: 判断 isModelSet(是不是又很眼熟,这也是Glide的load方法时赋值的),只有先调用了Glide.with().load(),这个标志位才会为true,否则就说明没有告诉Glide你要展示的是什么资源,从而就抛出异常 “You must call #load() before calling #into()”;

第四步: 第14行调用 buildRequest() 方法构建出了一个 Request对象;

第五步: 调用了 isEquivalentTo()isSkipMemoryCacheWithCompletePreviousRequest() 进行判断;

第六步: 调用了 requestManager.track() 方法来去执行这个Request。

下面我们就详细分析下第四、五、六步。


4、RequestBuilder.buildRequest()


第四步中调用 buildRequest() 方法构建出了一个 Request对象,Request 是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看 buildRequest() 方法是如何构建Request对象的:

public class RequestBuilder<TranscodeType> implements Cloneable,ModelTypes<RequestBuilder<TranscodeType>> {private Request buildRequest(Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,RequestOptions requestOptions) {return buildRequestRecursive(target,targetListener,/*parentCoordinator=*/ null,transitionOptions,requestOptions.getPriority(),requestOptions.getOverrideWidth(),requestOptions.getOverrideHeight(),requestOptions);}private Request buildRequestRecursive(Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,RequestOptions requestOptions) {// ... 忽略部分代码Request mainRequest =buildThumbnailRequestRecursive(target,targetListener,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight,requestOptions);if (errorRequestCoordinator == null) {return mainRequest;}// ... 忽略部分代码}private Request buildThumbnailRequestRecursive(Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,RequestOptions requestOptions) {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.requestOptions.isPrioritySet()? thumbnailBuilder.requestOptions.getPriority() : getThumbnailPriority(priority);int thumbOverrideWidth = thumbnailBuilder.requestOptions.getOverrideWidth();int thumbOverrideHeight = thumbnailBuilder.requestOptions.getOverrideHeight();if (Util.isValidDimensions(overrideWidth, overrideHeight)&& !thumbnailBuilder.requestOptions.isValidOverride()) {thumbOverrideWidth = requestOptions.getOverrideWidth();thumbOverrideHeight = requestOptions.getOverrideHeight();}ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);Request fullRequest =obtainRequest(target,targetListener,requestOptions,coordinator,transitionOptions,priority,overrideWidth,overrideHeight);isThumbnailBuilt = true;// Recursively generate thumbnail requests.Request thumbRequest =thumbnailBuilder.buildRequestRecursive(target,targetListener,coordinator,thumbTransitionOptions,thumbPriority,thumbOverrideWidth,thumbOverrideHeight,thumbnailBuilder.requestOptions);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(parentCoordinator);Request fullRequest =obtainRequest(target,targetListener,requestOptions,coordinator,transitionOptions,priority,overrideWidth,overrideHeight);RequestOptions thumbnailOptions = requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);Request thumbnailRequest =obtainRequest(target,targetListener,thumbnailOptions,coordinator,transitionOptions,getThumbnailPriority(priority),overrideWidth,overrideHeight);coordinator.setRequests(fullRequest, thumbnailRequest);return coordinator;} else {// Base case: no thumbnail.return obtainRequest(target,targetListener,requestOptions,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight);}}private Request obtainRequest(Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,RequestOptions requestOptions,RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight) {return SingleRequest.obtain(context,glideContext,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,glideContext.getEngine(),transitionOptions.getTransitionFactory());}
}

可以看到,buildRequest() 方法的内部其实又调用了 buildRequestRecursive() 方法,而 buildRequestRecursive() 方法中调用了 buildThumbnailRequestRecursive() 方法,该方法里调用了 obtainRequest() 方法来获取一个Request对象,而 obtainRequest() 方法中又去调用了 SingleRequest.obtain() 方法。


5、SingleRequest.obtain()


那么我们进入到这个SingleRequest的obtain()方法瞧一瞧:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {public static <R> SingleRequest<R> obtain(Context context,GlideContext glideContext,Object model,Class<R> transcodeClass,RequestOptions requestOptions,int overrideWidth,int overrideHeight,Priority priority,Target<R> target,RequestListener<R> targetListener,@Nullable List<RequestListener<R>> requestListeners,RequestCoordinator requestCoordinator,Engine engine,TransitionFactory<? super R> animationFactory) {@SuppressWarnings("unchecked") SingleRequest<R> request =(SingleRequest<R>) POOL.acquire();if (request == null) {request = new SingleRequest<>();}request.init(context,glideContext,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,engine,animationFactory);return request;}
}

可以看到,这里在第23行去new了一个 SingleRequest 对象,并在最后一行返回,也就是说,obtain() 方法实际上获得的就是一个SingleRequest对象。另外这里又在第25行调用了 SingleRequest的init() ,里面主要就是一些赋值的代码,将传入的这些参数赋值到SingleRequest的成员变量当中,我们就不再跟进去看了。


6、isEquivalentTo()、isSkipMemoryCacheWithCompletePreviousRequest()


我们继续回到into()方法中的第五步,调用了 isEquivalentTo()isSkipMemoryCacheWithCompletePreviousRequest() 进行判断,我们分别看俩家伙是干嘛的:


1、isEquivalentTo()

isEquivalentTo() 有三个具体实现,我们挑其中一个 SingleRequest 进行分析:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,...public boolean isEquivalentTo(Request o) {if (o instanceof SingleRequest) {SingleRequest<?> that = (SingleRequest<?>) o;return overrideWidth == that.overrideWidth&& overrideHeight == that.overrideHeight&& Util.bothModelsNullEquivalentOrEquals(model, that.model)&& transcodeClass.equals(that.transcodeClass)&& requestOptions.equals(that.requestOptions)&& priority == that.priority// We do not want to require that RequestListeners implement equals/hashcode, so we don't// compare them using equals(). We can however, at least assert that the same amount of// request listeners are present in both requests&& listenerCountEquals(this, that);}return false;}...
}

可以看到是在对比两个Request对象的参数是否完全一致。


2、isSkipMemoryCacheWithCompletePreviousRequest()

public class RequestBuilder<TranscodeType> implements Cloneable,ModelTypes<RequestBuilder<TranscodeType>> {...private boolean isSkipMemoryCacheWithCompletePreviousRequest(RequestOptions options, Request previous) {return !options.isMemoryCacheable() && previous.isComplete();}...
}

这里的 isMemoryCacheable() 其实就是判断当前Glide的缓存功能是否打开,也就是我们平时设置的 RequestOption#skipMemoryCache 方法传进来的boolean值。


7、RequestManager.track()


我们继续回到into()方法中的第六步,调用了 requestManager.track() 方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示:

public class RequestManager implements LifecycleListener,ModelTypes<RequestBuilder<Drawable>> {...void track(@NonNull Target<?> target, @NonNull Request request) {targetTracker.track(target);requestTracker.runRequest(request);}...
}

track() 方法里调用了 requestTracker.runRequest() 方法,那么我们继续跟进去瞧一瞧,如下所示:

public class RequestTracker {...public void runRequest(@NonNull Request request) {requests.add(request);if (!isPaused) {request.begin();} else {request.clear();if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Paused, delaying request");}pendingRequests.add(request);}}...
}

这里有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。


8、SingleRequest.begin()


暂停请求的功能仍然不是这篇文章所关心的,这里就直接忽略了,我们重点来看这个 begin() 方法。由于当前的Request对象是一个SingleRequest,因此这里就需要看 SingleRequest中的begin() 方法了,如下所示:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {... @Overridepublic void begin() {assertNotCallingCallbacks();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;}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));}}...
}

可以看到,其中基本都是根据状态的判断来做一些操作:

第一步: 判断model是否为空,这里的model正是我们上一步buildRequest传进来的参数,即代表着我们要加载的资源,也就是我们load()方法中传入的参数,因此model等于空肯定是加载失败的;

第二步: 判断是否正在执行中,不重复执行;

第三步: 判断是否已经是加载完成状态,是的话调用onResourceReady,上面这个过程有Target的几个关键的函数:onLoadFailedonResourceReadyonLoadStarted,我们以 onLoadFailed 为例:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {...private void onLoadFailed(GlideException e, int maxLogLevel) {stateVerifier.throwIfRecycled();int logLevel = glideContext.getLogLevel();if (logLevel <= maxLogLevel) {Log.w(GLIDE_TAG, "Load failed for " + model + " with size [" + width + "x" + height + "]", e);if (logLevel <= Log.INFO) {e.logRootCauses(GLIDE_TAG);}}loadStatus = null;status = Status.FAILED;isCallingCallbacks = true;try {//TODO: what if this is a thumbnail request?boolean anyListenerHandledUpdatingTarget = false;if (requestListeners != null) {for (RequestListener<R> listener : requestListeners) {anyListenerHandledUpdatingTarget |=listener.onLoadFailed(e, model, target, isFirstReadyResource());}}anyListenerHandledUpdatingTarget |=targetListener != null&& targetListener.onLoadFailed(e, model, target, isFirstReadyResource());if (!anyListenerHandledUpdatingTarget) {setErrorPlaceholder();}} finally {isCallingCallbacks = false;}notifyLoadFailed();}...
}

我们跟到 onLoadFailed() 方法里面去看看,你会发现它最终会调用到一个 setErrorPlaceholder() 当中,如下所示:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {...private void setErrorPlaceholder() {if (!canNotifyStatusChanged()) {return;}Drawable error = null;if (model == null) {error = getFallbackDrawable();}// Either the model isn't null, or there was no fallback drawable set.if (error == null) {error = getErrorDrawable();}// The model isn't null, no fallback drawable was set or no error drawable was set.if (error == null) {error = getPlaceholderDrawable();}target.onLoadFailed(error);}...
}

这个方法中会先去获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用 target.onLoadFailed() 方法并将占位图传入,这里的target其实就是ImageViewTarget(之前提到ImageViewTarget是用来展示图片的),那么 onLoadFailed() 方法中做了什么呢?我们看一下:


9、ImageViewTarget.onLoadFailed()


public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {...@Overridepublic void setDrawable(Drawable drawable) {view.setImageDrawable(drawable);}@Overridepublic void onLoadFailed(@Nullable Drawable errorDrawable) {super.onLoadFailed(errorDrawable);setResourceInternal(null);setDrawable(errorDrawable);}...
}

很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才begin()方法的第52行,你会发现它又调用了一个 target.onLoadStarted() 方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的 RequestOptions 中的 placeholder()error() 这两个占位图API底层的实现原理。


10、ImageViewTarget.onLoadStarted()、onResourceReady()


同理 onResourceReadyonLoadStarted 最终也是追溯到ImageViewTarget类中:

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {...@Overridepublic void setDrawable(Drawable drawable) {view.setImageDrawable(drawable);}@Overridepublic void onLoadStarted(@Nullable Drawable placeholder) {super.onLoadStarted(placeholder);setResourceInternal(null);setDrawable(placeholder);}@Overridepublic 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);}protected abstract void setResource(@Nullable Z resource);...
}

onResourceReady() 最终调用了setResource()方法,该方法是一个抽象方法,该方法实现在BitmapImageViewTarget类中:

public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {...@Overrideprotected void setResource(Bitmap resource) {view.setImageBitmap(resource);}...
}

到这里虽然找到了展示图片的真正地方,但是很多小伙伴会疑惑,这是最终的展示,那那请求资源的过程去哪啦?我们回到刚才的 begin() 方法:

在这里插入图片描述
刚才分析的都是图片正在加载中或者加载完成时的状态,那些状态下都是可以直接展示的,所以剩下的就是它的加载过程了,是在 begin() 方法的第45行和第47行。这里要分两种情况,一种是你在RequestOptions中使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第45行代码,调用 onSizeReady() 方法。如果没指定的话,就会执行第47行代码,调用 target.getSize() 方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。具体的计算细节我就不带着大家分析了,总之在计算完之后,它也会调用 onSizeReady() 方法。也就是说,不管是哪种情况,最终都会调用到onSizeReady()方法,在这里进行下一步操作。那么我们跟到这个方法里面来:


11、SingleRequest.onSizeReady()


public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {...@Overridepublic void onSizeReady(int width, int height) {stateVerifier.throwIfRecycled();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);// 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));}}...
}

onSizeReady() 方法中首先把State状态更改为RUNNING,然后根据我们通过 RequestOptions#sizeMultiplier 设置的sizeMultiplier,它相当于图片的一个缩放系数,来计算出相应的宽高,顺便提一句,Glide框架会根据请求加载图片的ImageView的宽高来进行加载相对应的宽高图片,每次根据view的大小加载的图片是不一定一样的。最后调用 engine 中的 load() 方法,这个 Engine的load() 方法当中,到底做了什么?代码如下所示:


12、Engine.load()


public class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,EngineResource.ResourceListener {...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) {Util.assertMainThread();long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,resourceClass, transcodeClass, options);EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);if (active != null) {cb.onResourceReady(active, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return null;}EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);if (cached != null) {cb.onResourceReady(cached, DataSource.MEMORY_CACHE);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return null;}EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb);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);engineJob.start(decodeJob);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);}...
}

所有重要的东西都在这里了,解析下:

  • 判断是否在主线程运行,说明到目前为止还是在主线程执行的,并没有真正的开启子线程。

  • 通过 keyFactory 工厂来构建一个 EngineKey 对象,key关联着model,也就是url,它很根据model,view的宽高等等属性来构建一个 EngineKey 对象,这个对象可以用来指定缓存地址,可以用来从缓存中查找资源等。

  • 根据创建的key对象分别调用 loadFromCacheloadFromActiveResources 方法来从内存中查找是否有缓存资源,如果有,则回调 cb.onResourceReady 来直接设置图片了。

  • 分别使用 engineJobFactorydecodeJobFactory 构建 EngineJobDecodeJob 对象,这两个对象是真正的加载资源的两个重要类EngineJob 对象负责开启线程去加载资源,并且加载得资源后转换到主线程并进行回调;DecodeJob 是真正的执行者,它就是去网络加载资源的地方,EngineJob 开启线程,真正执行的是 DecodeJobDecodeJob 之后完毕之后叫道 EngineJob 去分发回调。这就是这两个类的关系。

  • EngineJobDecodeJob 的构建是基本一致的,我们看看比较复杂的 DecodeJob 的构建:在build方法中,首先通过pool来创建一个 DecodeJob 对象,然后调用 DecodeJob 对象的init方法进行初始化,在初始化中值得注意的是调用了 decodeHelper 对象的init方法。decodeHelper 方法是 DecodeJob 的重要辅助类,后面我们会详细的接触它。

  • 上面也提到回调,这里先cb添加到 engineJob.addCallback() 中,然后调用 EngineJob 的start方法来开启线程。

下面我们来看看start方法中的源码:

class EngineJob<R> implements DecodeJob.Callback<R>,Poolable {...public void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;GlideExecutor executor = decodeJob.willDecodeFromCache()? diskCacheExecutor: getActiveSourceExecutor();executor.execute(decodeJob);}...
}

start 方法中将会调用 GlideExecutorexecute 方法:

public final class GlideExecutor implements ExecutorService {...@Overridepublic void execute(@NonNull Runnable command) {delegate.execute(command);}...
}

execute 中正式的开启了线程池进行加载资源。由此我们也正式的由主线程转到了子线程中。

上面我们也分析了,真正执行线程的是在 DecodeJob 类中,那么我们去看看的 run 方法是怎么执行的:


13、DecodeJob.run()


class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...@Overridepublic 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 (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;}} 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();}}
}

这个方法中的代码并不多,但我们仍然还是要抓重点。在第22行,这里调用了一个 runWrapped() 方法,看上去所有的逻辑应该都在这个 runWrapped() 方法执行的了,那我们跟进去瞧一瞧:


14、DecodeJob.runWrapped()


class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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);}}...
}

我们知道,在构造 DecodeJob 时调用init方法是 runReason 被赋值为 INITIALIZE 值,由此它将会进入到 INITIALIZE 分支中,调用 getNextStage 方法:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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);}}...
}

getNextStage 方法中经错几次的经转会返回 Stage.SOURCE 值,然后在调用 getNextGenerator 方法来获取当前的 currentGenerator 对象,我们在来看看获取的这个 currentGenerator 到底是什么?

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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);}}...
}

getNextGenerator 方法中根据 stage 值来创建对象,由此我们可以知道 currentGenerator 是一个 SourceGenerator 对象,那么我们继续往下走,来看看 runGenerators 方法:


15、DecodeJob.runGenerators()


class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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;}}// 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.}...
}

runGenerators 最重要的就是执行了 currentGeneratorstartNext() 方法,这里将会真正的去加载网络资源。

我们从上面知道 currentGenerator 就是 SourceGenerator 对象,那么我们去看看 SourceGeneratorstartNext 的实现:


16、SourceGenerator.startNext()


class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,DataFetcherGenerator.FetcherReadyCallback {...@Overridepublic 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;}...
}

到这里就太关键了,因为很多人都死在这一步上,根本找不到是怎么去加载资源的,是怎么执行到我们熟悉的 HttpURLConnection 的,那么到这里你千万不能分神,稍微分神就会转迷糊了,哈哈,那么接下来就开始分析吧。

由于我们在创建 SourceGenerator 对象时,只是传递了 DecodeHelper回调cb对象 ,其他的一切初始化操作都是不存在的,所以在 startNext 中,前面的两个判断是不成立的,主要是看while循环里面的内容。

while循环中首先调用 hasNextModelLoader 进行判断,我们来看下 hasNextModelLoader 的内容,这里必须集中精力理解清楚,不然肯定不知所云:

class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,DataFetcherGenerator.FetcherReadyCallback {...private boolean hasNextModelLoader() {return loadDataListIndex < helper.getLoadData().size();}...
}

hasNextModelLoader 中它要去 helper 加载数据,调用 getLoadData 方法,我们跟进去看看:

final class DecodeHelper<Transcode> {...List<LoadData<?>> getLoadData() {if (!isLoadDataSet) {isLoadDataSet = true;loadData.clear();List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = modelLoaders.size(); i < size; i++) {ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);LoadData<?> current =modelLoader.buildLoadData(model, width, height, options);if (current != null) {loadData.add(current);}}}return loadData;}...
}

getLoadData 方法中是调用 GlideContext 中的 getRegistry 方法来回去 Registry 对象,它是在Glide的构造方法中创建的,而且注册添加了很多解析器还记得吗?不记得的赶快去看看Glide的构造方法。这里我们插入一点非常重要的分析,理解清楚它对接下来的网络执行非常的重要,回到Glidet的构造方法来看看Registry中几个特殊的解析类:

public class Glide implements ComponentCallbacks2 {...Glide(@NonNull Context context,@NonNull Engine engine,@NonNull MemoryCache memoryCache,@NonNull BitmapPool bitmapPool,@NonNull ArrayPool arrayPool,@NonNull RequestManagerRetriever requestManagerRetriever,@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,int logLevel,@NonNull RequestOptions defaultRequestOptions,@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {registry.append(xx)....append(Uri.class, InputStream.class, new HttpUriLoader.Factory())....append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())...}...
}

还有印象这段很长的代码吧?

我们重点来看看上面的两个 append 方法,其他的都是一样或是类似的道理的。跟进去 registryappend 方法中:

public class Registry {...@NonNullpublic <Model, Data> Registry append(@NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass,@NonNull ModelLoaderFactory<Model, Data> factory) {modelLoaderRegistry.append(modelClass, dataClass, factory);return this;}...
}

继续进入 modelLoaderRegistry.append 方法中:

public class ModelLoaderRegistry {...public synchronized <Model, Data> void append(@NonNull Class<Model> modelClass,@NonNull Class<Data> dataClass,@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {multiModelLoaderFactory.append(modelClass, dataClass, factory);cache.clear();}...
}

再跟进 multiModelLoaderFactory.append 方法中:

public class MultiModelLoaderFactory {...synchronized <Model, Data> void append(@NonNull Class<Model> modelClass,@NonNull Class<Data> dataClass,@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {add(modelClass, dataClass, factory, /*append=*/ true);}...
}

重点来了,最后进去add方法:

public class MultiModelLoaderFactory {...private <Model, Data> void add(@NonNull Class<Model> modelClass,@NonNull Class<Data> dataClass,@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,boolean append) {Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);entries.add(append ? entries.size() : 0, entry);}...
}
public class MultiModelLoaderFactory {...private static class Entry<Model, Data> {private final Class<Model> modelClass;@Synthetic final Class<Data> dataClass;@Synthetic final ModelLoaderFactory<? extends Model, ? extends Data> factory;public Entry(@NonNull Class<Model> modelClass,@NonNull Class<Data> dataClass,@NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {this.modelClass = modelClass;this.dataClass = dataClass;this.factory = factory;}public boolean handles(@NonNull Class<?> modelClass, @NonNull Class<?> dataClass) {return handles(modelClass) && this.dataClass.isAssignableFrom(dataClass);}public boolean handles(@NonNull Class<?> modelClass) {return this.modelClass.isAssignableFrom(modelClass);}}...
}

创建了一个 Entry 对象,把我们的 modelClassdataClassfactory 对象关联起来,然后存放到entries的list集合中,就这么简单,但是对这个Entry对象的理解关系到我们后面对整个网络加载的流程十分的巨大,ok,到这里,我们插入的讲解已经完了,主要想告诉你的就是这个entries集合包含了那些对象和创建Entry对象所关联的类和工厂。

那么现在回到 DecodeHelper 中的 getLoadData 方法中,它从 GlideContext 获取到 Registry 对象,Registry 对象有哪些内容在上面的插入讲解中也已举特例分析了,然后调用 getModelLoaders 方法,并传进model对象,那么来看看它是怎么实现的:

public class Registry {...public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);if (result.isEmpty()) {throw new NoModelLoaderAvailableException(model);}return result;}...
}

它会从 modelLoaderRegistry 中获取,在来看:

public class ModelLoaderRegistry {...public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));int size = modelLoaders.size();boolean isEmpty = true;List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0; i < size; i++) {ModelLoader<A, ?> loader = modelLoaders.get(i);if (loader.handles(model)) {if (isEmpty) {filteredLoaders = new ArrayList<>(size - i);isEmpty = false;}filteredLoaders.add(loader);}}return filteredLoaders;}...
}

它会通过model从 getModelLoadersForClass 方法中获取到 modelLoaders 的集合,来看看:

public class ModelLoaderRegistry {...@NonNullprivate synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(@NonNull Class<A> modelClass) {List<ModelLoader<A, ?>> loaders = cache.get(modelClass);if (loaders == null) {loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));cache.put(modelClass, loaders);}return loaders;}...
}

首先从cache缓存中获取,如果为空,将会从 multiModelLoaderFactory 工厂中获取,在继续跟进 multiModelLoaderFactory 的build方法看看:

public class MultiModelLoaderFactory {...synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {try {List<ModelLoader<Model, ?>> loaders = new ArrayList<>();for (Entry<?, ?> entry : entries) {// Avoid stack overflow recursively creating model loaders by only creating loaders in// recursive requests if they haven't been created earlier in the chain. For example:// A Uri loader may translate to another model, which in turn may translate back to a Uri.// The original Uri loader won't be provided to the intermediate model loader, although// other Uri loaders will be.if (alreadyUsedEntries.contains(entry)) {continue;}if (entry.handles(modelClass)) {alreadyUsedEntries.add(entry);loaders.add(this.<Model, Object>build(entry));alreadyUsedEntries.remove(entry);}}return loaders;} catch (Throwable t) {alreadyUsedEntries.clear();throw t;}}...
}

从entries集合中分别的遍历出entry对象,然后调用entry.handles来进行匹配是否符合,来看handles方法:

public class MultiModelLoaderFactory {...private static class Entry<Model, Data> {...public boolean handles(@NonNull Class<?> modelClass) {return this.modelClass.isAssignableFrom(modelClass);}}...
}

this.modelClass 是什么还记得吗?没错就是在 Glide 创建 Registry 对象是 appendXX.class ,例如:

.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())

modelClass 呢,这里就是我们传递用来加载网络图片的一个url地址,那么调用 isAssignableFrom 方法进行匹配,我们知道entries包含很多的解析器,所以在这一步将会排除掉不匹配的解析器,然后调用调用build方法来创建加载器:

public class MultiModelLoaderFactory {...private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));}...
}

entry.factory知道是什么吧?没错就是append方法中new的一个工厂类。

还是以下面的特例来说:

.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())

在这里这个 factory 就是 HttpUriLoader.Factory ,我们来看看它是怎么实现的:

public class HttpUriLoader implements ModelLoader<Uri, InputStream> {...public static class Factory implements ModelLoaderFactory<Uri, InputStream> {public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));}...}...
}

这里它将会 multiFactory.build 方法来构造一个 ModelLoader 对象,看清楚它传递的参数: GlideUrl.class , InputStream.class ,然后跟进去查看:

public class MultiModelLoaderFactory {...public synchronized <Model, Data> ModelLoader<Model, Data> build(@NonNull Class<Model> modelClass,@NonNull Class<Data> dataClass) {try {List<ModelLoader<Model, Data>> loaders = new ArrayList<>();boolean ignoredAnyEntries = false;for (Entry<?, ?> entry : entries) {// Avoid stack overflow recursively creating model loaders by only creating loaders in// recursive requests if they haven't been created earlier in the chain. For example:// A Uri loader may translate to another model, which in turn may translate back to a Uri.// The original Uri loader won't be provided to the intermediate model loader, although// other Uri loaders will be.if (alreadyUsedEntries.contains(entry)) {ignoredAnyEntries = true;continue;}if (entry.handles(modelClass, dataClass)) {alreadyUsedEntries.add(entry);loaders.add(this.<Model, Data>build(entry));alreadyUsedEntries.remove(entry);}}if (loaders.size() > 1) {return factory.build(loaders, throwableListPool);} else if (loaders.size() == 1) {return loaders.get(0);} else {// Avoid crashing if recursion results in no loaders available. The assertion is supposed to// catch completely unhandled types, recursion may mean a subtype isn't handled somewhere// down the stack, which is often ok.if (ignoredAnyEntries) {return emptyModelLoader();} else {throw new NoModelLoaderAvailableException(modelClass, dataClass);}}} catch (Throwable t) {alreadyUsedEntries.clear();throw t;}}...
}

历史总是惊人的相似,再次从entries中获取Entry对象,然后调用entry.handles方法根据GlideUrl.class, InputStream.class这两个参数进行匹配过滤。

然后我们找到了以下的append内容相匹配的Entry对象:

.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

找到 HttpGlideUrlLoader.Factory 之后,然后调用build方法去构建,在build方法中同样的方式调用 entry.factory.build(this) ,来看看 HttpGlideUrlLoader.Factory() 的build的源码:

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {...public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {return new HttpGlideUrlLoader(modelCache);}...}...
}

直接的创建一个 HttpGlideUrlLoader 对象并返回。

到此我们获取了真正的图片加载对象,然后我们回到 HttpUriLoader 的Factory中,在 multiFactory.build(GlideUrl.class, InputStream.class) 获取到 HttpGlideUrlLoader 对象后,并传递到创建的 HttpUriLoader 对象中去,我们来看看:

public class HttpUriLoader implements ModelLoader<Uri, InputStream> {private static final Set<String> SCHEMES =Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));private final ModelLoader<GlideUrl, InputStream> urlLoader;// Public API.@SuppressWarnings("WeakerAccess")public HttpUriLoader(ModelLoader<GlideUrl, InputStream> urlLoader) {this.urlLoader = urlLoader;}...
}

HttpGlideUrlLoader 对象赋值给 HttpUriLoader 的成员变量 this.urlLoader 中。

ok,到此我们真正的加载器已经获取到了,当然并不是只有一个,可能有多个,因为在Registry注册了多个可以解析Uri.class的解析器。

好,我们在回到 ModelLoaderRegistry 类中的 getModelLoaders 方法中,从 getModelLoadersForClass 方法中我们获取到了可以解析我们请求modle的所有解析器,通过for循环遍历出所有的解析器,存放到 filteredLoaders 集合中并返回,一直返回到 DecodeHelper 类中的 getLoadData 方法中。

final class DecodeHelper<Transcode> {...List<LoadData<?>> getLoadData() {if (!isLoadDataSet) {isLoadDataSet = true;loadData.clear();List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = modelLoaders.size(); 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;}...
}

然后遍历 modelLoaders 集合,分别获取 ModelLoader 对象,并调用 buildLoadData 方法,我们知道 modelLoaders 集合中一定会包含一个 ModelLoaderHttpUriLoader ,那来看看它的 buildLoadData 方法:

public class HttpUriLoader implements ModelLoader<Uri, InputStream> {...public LoadData<InputStream> buildLoadData(@NonNull Uri model, int width, int height,@NonNull Options options) {return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);}...
}

它会调用 urlLoader.buildLoadData 方法,这个 urlLoader 就是 HttpGlideUrlLoader 对象,再来看看:

public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {...public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,@NonNull Options options) {// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time// spent parsing urls.GlideUrl url = model;if (modelCache != null) {url = modelCache.get(model, 0, 0);if (url == null) {modelCache.put(model, 0, 0, model);url = model;}}int timeout = options.get(TIMEOUT);return new LoadData<>(url, new HttpUrlFetcher(url, timeout));}...
}

这里最重要的就是创建个一个 HttpUrlFetcher 对象:

public class HttpUrlFetcher implements DataFetcher<InputStream> {...public HttpUrlFetcher(GlideUrl glideUrl, int timeout) {this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);}@VisibleForTestingHttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {this.glideUrl = glideUrl;this.timeout = timeout;this.connectionFactory = connectionFactory;}...
}

然后把 HttpUrlFetcher 对象存放到新建的 LoadData 对象中:

public interface ModelLoader<Model, Data> {class LoadData<Data> {public final Key sourceKey;public final List<Key> alternateKeys;public final DataFetcher<Data> fetcher;public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {this(sourceKey, Collections.<Key>emptyList(), fetcher);}public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,@NonNull DataFetcher<Data> fetcher) {this.sourceKey = Preconditions.checkNotNull(sourceKey);this.alternateKeys = Preconditions.checkNotNull(alternateKeys);this.fetcher = Preconditions.checkNotNull(fetcher);}}...
}

最后把 LoadData 对象返回,我们往上返回到 SourceGeneratorstartNext 方法中:

class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,DataFetcherGenerator.FetcherReadyCallback {...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;}...
}

在获取到 LoadData 对象后,调用 loadData.fetcher.loadData(helper.getPriority(), this); 这个方法,从上面分析 loadData.fetcher 就是 HttpUrlFetcher 对象,那我们来看看它里面的loadData是怎么加载数据的:


17、HttpUrlFetcher.loadData()


public class HttpUrlFetcher implements DataFetcher<InputStream> {...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());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));}}}...
}

它会调用 loadDataWithRedirects 方法来返回一个 InputStream 输入流,来看看 loadDataWithRedirects 的源码:


18、HttpUrlFetcher.loadDataWithRedirects()


public class HttpUrlFetcher implements DataFetcher<InputStream> {...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);}}...
}

在获取到 InputStream 输入流之后,最会将会调用 callback.onDataReady(result); 回调方法,并把输入流传递过去。

那这个callback是什么呢?

还记得 loadData.fetcher.loadData(helper.getPriority(), this); 这段代码吧?

没错他就是 SourceGenerator 类实现的 DataCallback 回调类。

那么在进到 SourceGenerator 找到 onDataReady 方法吧:

class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,DataFetcherGenerator.FetcherReadyCallback {...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);}}...
}

在这里它又调用cb回调类的 onDataFetcherReady 方法,并传递了相关参数: loadData.fetcherHttpUrlFetcherloadData.fetcher.getDataSource() 则是 DataSource.REMOTE

public class HttpUrlFetcher implements DataFetcher<InputStream> {...public DataSource getDataSource() {return DataSource.REMOTE;}...
}

那么这个cb又是什么呢,它是 FetcherReadyCallback 回调类,在 DecodeJob 中实现,那么回到 DecodeJobonDataFetcherReady 方法中:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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) {runReason = RunReason.DECODE_DATA;callback.reschedule(this);} else {GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");try {decodeFromRetrievedData();} finally {GlideTrace.endSection();}}}...
}

onDataFetcherReady 方法中保存了相关的参数变量,判断是否是当前线程,然后调用 decodeFromRetrievedData 方法来解码数据:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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();}}...
}

首先创建一个 Resource 类型的变量,通过 decodeFromData 方法把输入流解码并返回给resource,由此可也看出,解码主要是在 decodeFromData 方法中:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,DataSource dataSource) throws GlideException {try {if (data == null) {return null;}long startTime = LogTime.getLogTime();Resource<R> result = decodeFromFetcher(data, dataSource);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Decoded result " + result, startTime);}return result;} finally {fetcher.cleanup();}}...
}

在这调用 decodeFromFetcher 方法来进行解码返回一个 Resource

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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);}...
}

在这里获取到 data.getClass ,这个Class就是 InputStrem.class ,那么在调用 decodeHelper.getLoadPath 方法后,我们来看看做了哪些操作:

final class DecodeHelper<Transcode> {...<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);}...
}

getLoadPath 方法啥也没做,直接调用 Registry 中的 getLoadPath ,并传递了拥有的变量,其中 resourceClassObject.classtranscodeClass 则是 Drawable.class ,这是在前面已构建初始化好的,直接拿来用。

我们在来跟进去看看:

public class Registry {...public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,@NonNull Class<Transcode> transcodeClass) {LoadPath<Data, TResource, Transcode> result =loadPathCache.get(dataClass, resourceClass, transcodeClass);if (loadPathCache.isEmptyLoadPath(result)) {return null;} else if (result == null) {List<DecodePath<Data, TResource, Transcode>> decodePaths =getDecodePaths(dataClass, resourceClass, transcodeClass);// It's possible there is no way to decode or transcode to the desired types from a given// data class.if (decodePaths.isEmpty()) {result = null;} else {result =new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);}loadPathCache.put(dataClass, resourceClass, transcodeClass, result);}return result;}...
}

这里首先从 loadPathCache 缓存中获取 LoadPath 对象,如果没有则调用 getDecodePaths 方法进行获取:

public class Registry {...private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,@NonNull Class<Transcode> transcodeClass) {List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();List<Class<TResource>> registeredResourceClasses =decoderRegistry.getResourceClasses(dataClass, resourceClass);for (Class<TResource> registeredResourceClass : registeredResourceClasses) {List<Class<Transcode>> registeredTranscodeClasses =transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {List<ResourceDecoder<Data, TResource>> decoders =decoderRegistry.getDecoders(dataClass, registeredResourceClass);ResourceTranscoder<TResource, Transcode> transcoder =transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")DecodePath<Data, TResource, Transcode> path =new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,decoders, transcoder, throwableListPool);decodePaths.add(path);}}return decodePaths;}...
}

getDecodePaths 方法中还是很重要的,对整个解码过程的理解有很大的帮助,所以我们来认真的分析下:

① 首先从 decoderRegistry.getResourceClasses 方法中获取已经注册的 registeredResourceClasses

public class ResourceDecoderRegistry {...public synchronized <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T> dataClass,@NonNull Class<R> resourceClass) {List<Class<R>> result = new ArrayList<>();for (String bucket : bucketPriorityList) {List<Entry<?, ?>> entries = decoders.get(bucket);if (entries == null) {continue;}for (Entry<?, ?> entry : entries) {if (entry.handles(dataClass, resourceClass)&& !result.contains((Class<R>) entry.resourceClass)) {result.add((Class<R>) entry.resourceClass);}}}return result;}...
}
public class ResourceDecoderRegistry {...private static class Entry<T, R> {...public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?> resourceClass) {return this.dataClass.isAssignableFrom(dataClass) && resourceClass.isAssignableFrom(this.resourceClass);}...}...
}

还记得我们创建Registry是注册了一大堆的东西吗?

public class Glide implements ComponentCallbacks2 {...Glide(@NonNull Context context,@NonNull Engine engine,@NonNull MemoryCache memoryCache,@NonNull BitmapPool bitmapPool,@NonNull ArrayPool arrayPool,@NonNull RequestManagerRetriever requestManagerRetriever,@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,int logLevel,@NonNull RequestOptions defaultRequestOptions,@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {registry....append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)....append(Registry.BUCKET_BITMAP_DRAWABLE,InputStream.class,BitmapDrawable.class,new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))....append(Registry.BUCKET_GIF,InputStream.class,GifDrawable.class,new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))...}...
}

通过 handles 方法对 InputStrem.classObject.class 进行匹配的。由此我们可以得出 registeredResourceClasses 集合中分别对应的是 Bitmap.classBitmapDrawable.classGifDrawable.class 的三种Class对象。

② 遍历 registeredResourceClasses 集合,通过 transcoderRegistry.getTranscodeClasses 方法获取到已注册的 registeredTranscodeClasses 集合:

public class TranscoderRegistry {...public synchronized <Z, R> List<Class<R>> getTranscodeClasses(@NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) {List<Class<R>> transcodeClasses = new ArrayList<>();// GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable.if (transcodeClass.isAssignableFrom(resourceClass)) {transcodeClasses.add(transcodeClass);return transcodeClasses;}for (Entry<?, ?> entry : transcoders) {if (entry.handles(resourceClass, transcodeClass)) {transcodeClasses.add(transcodeClass);}}return transcodeClasses;}...
}

这是对 registeredResourceClasses 集合的再次匹配,我们知道 transcodeClasses 实际上是 Drawable.class ,在 registeredResourceClasses 集合中只有 BitmapDrawable.classGifDrawable.class 是继承 Drawable 的,由此我们得出 registeredTranscodeClasses 包含 BitmapDrawable.classGifDrawable.class 两种 Class 对象。


那么由此可知decoders存放了两种解码器:

  • 对应的 BitmapDrawableBitmapDrawableDecoder 解码器。

  • 对应的 GifDrawable.classStreamGifDecoder 解码器。

这个非常重要,在后面有使用到。必须理解清楚。


③ 通过上面获取的 registeredResourceClassregisteredTranscodeClass 获取到 transcoder 转化器,主要是从 transcoderRegistry 集合中通过get方法获取,而 transcoderRegistry 又包含哪些转化器呢?

从get方法我们可以知道:

public class TranscoderRegistry {...public synchronized <Z, R> ResourceTranscoder<Z, R> get(@NonNull Class<Z> resourceClass, @NonNull Class<R> transcodedClass) {// For example, there may be a transcoder that can convert a GifDrawable to a Drawable, which// will be caught above. However, if there is no registered transcoder, we can still just use// the UnitTranscoder to return the Drawable because the transcode class (Drawable) is// assignable from the resource class (GifDrawable).if (transcodedClass.isAssignableFrom(resourceClass)) {return (ResourceTranscoder<Z, R>) UnitTranscoder.get();}for (Entry<?, ?> entry : transcoders) {if (entry.handles(resourceClass, transcodedClass)) {return (ResourceTranscoder<Z, R>) entry.transcoder;}}throw new IllegalArgumentException("No transcoder registered to transcode from " + resourceClass + " to " + transcodedClass);}...
}

当时普通图片是 transcoder 将会是 BitmapDrawableTranscoder ,如是过动态图片gif的话transcoder将会是 GifDrawableBytesTranscoder

然后把它们存放到创建的 DecodePath 对象中:

public class DecodePath<DataType, ResourceType, Transcode> {...public DecodePath(Class<DataType> dataClass, Class<ResourceType> resourceClass,Class<Transcode> transcodeClass,List<? extends ResourceDecoder<DataType, ResourceType>> decoders,ResourceTranscoder<ResourceType, Transcode> transcoder, Pool<List<Throwable>> listPool) {this.dataClass = dataClass;this.decoders = decoders;this.transcoder = transcoder;this.listPool = listPool;failureMessage = "Failed DecodePath{" + dataClass.getSimpleName() + "->"+ resourceClass.getSimpleName() + "->" + transcodeClass.getSimpleName() + "}";}...
}

最后是把封装好的 DecodePath 对象存储到 decodePaths 集合中并返回。

然后在回到 Registry 中的 getLoadPath 方法中,在获取到 decodePaths 集合中,把它和 dataClassresourceClasstranscodeClass 又封装到 LoadPath 对象中,并缓存到 loadPathCache 对象中。

再次返回 DecodeJob 类中的 decodeFromFetcher 方法中,在获取到 LoadPath 后,调用 **runLoadPath`** 方法,来看下它的源码:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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.return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));} finally {rewinder.cleanup();}}...
}

它调用 glideContext.getRegistry() 获取 Registry 对象,然后调用 RegistrygetRewinder 获取装置器,跟进去看看:

public class Registry {...public <X> DataRewinder<X> getRewinder(@NonNull X data) {return dataRewinderRegistry.build(data);}...
}

getRewinder 方法中直接的调用 dataRewinderRegistry 对象的 build 方法,那么这个 dataRewinderRegistry 又是什么呢?

它是在 Registry 构造方法中创建的对象:

public class Registry {...public Registry() {...this.dataRewinderRegistry = new DataRewinderRegistry();...);}...
}

而且在 Glide 中进行了注册:

public class Glide implements ComponentCallbacks2 {...Glide(@NonNull Context context,@NonNull Engine engine,@NonNull MemoryCache memoryCache,@NonNull BitmapPool bitmapPool,@NonNull ArrayPool arrayPool,@NonNull RequestManagerRetriever requestManagerRetriever,@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,int logLevel,@NonNull RequestOptions defaultRequestOptions,@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {registry.../* Files */.register(new ByteBufferRewinder.Factory())....register(new InputStreamRewinder.Factory(arrayPool))...}...
}
public class Registry {...public Registry register(@NonNull DataRewinder.Factory<?> factory) {dataRewinderRegistry.register(factory);return this;}...
}

获取到 factory 之后调用 dataRewinderRegistry.register 方法注册到Map集合 rewinders 中去:

public class DataRewinderRegistry {...public synchronized void register(@NonNull DataRewinder.Factory<?> factory) {rewinders.put(factory.getDataClass(), factory);}...
}

再来看看 factory.getDataClass() 都获取到哪些key:

  • ① 在 ByteBufferRewinder.Factory 中调用 getDataClass() 获取的key是 ByteBuffer.class

  • ② 在 InputStreamRewinder.Factory 中调用 getDataClass() 获取的key是 InputStream.class

上面两个key值在接下来会使用到。

那么我们在返回到 getRewinder 方法中,调用 dataRewinderRegistry 对象的build方法到底做了什么:

public class DataRewinderRegistry {...public synchronized <T> DataRewinder<T> build(@NonNull T data) {Preconditions.checkNotNull(data);DataRewinder.Factory<T> result = (DataRewinder.Factory<T>) rewinders.get(data.getClass());if (result == null) {for (DataRewinder.Factory<?> registeredFactory : rewinders.values()) {if (registeredFactory.getDataClass().isAssignableFrom(data.getClass())) {result = (DataRewinder.Factory<T>) registeredFactory;break;}}}if (result == null) {result = (DataRewinder.Factory<T>) DEFAULT_FACTORY;}return result.build(data);}...
}

在build的方法中总共做了四件事:

  • ① 从 rewinders 集合中获取到和 data.getClass() 相匹配的 Factory 对象,从上面的分析中,我们知道 rewinders 注册的只有两个key,分别是 ByteBuffer.classInputStream.class ,而我们又知道 data.getClass() 是一个 InputStream.class ,由此可以匹配成功。这个result就是 InputStreamRewinder.Factory 对象。

  • ② 假如 result 没有匹配成功的话,也就是没有通过key匹配成功,那么就进行遍历 rewinders 集合,通过values值进行匹配,把匹配成功的Factory对象再赋值给result。

  • ③ 假如通过键key和值values都没有匹配成功,那么也不要紧,直接使用默认的 DEFAULT_FACTORY

  • ④ 最后调用 resultbuild 方法。

这里由于我们通过键key直接已匹配成功,所以我们知道result就是 InputStreamRewinder.Factory 对象,那么来看看调用它的build方法做了什么事情:

public final class InputStreamRewinder implements DataRewinder<InputStream> {...public static final class Factory implements DataRewinder.Factory<InputStream> {...public DataRewinder<InputStream> build(InputStream data) {return new InputStreamRewinder(data, byteArrayPool);}public Class<InputStream> getDataClass() {return InputStream.class;}}...
}

这里直接的返回了一个 InputStreamRewinder 对象,在 InputStreamRewinder 的构造方法中又创建了 RecyclableBufferedInputStream 对象,并它 InputStream 流传递进去:

public final class InputStreamRewinder implements DataRewinder<InputStream> {// 5mb.private static final int MARK_LIMIT = 5 * 1024 * 1024;private final RecyclableBufferedInputStream bufferedStream;@SyntheticInputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);bufferedStream.mark(MARK_LIMIT);}...
}

RecyclableBufferedInputStream 继承 FilterInputStream 流,那么我们的 InputStream 流最终保存的位置就是在 FilterInputStream 类中。

ok,到这里我们知道了 InputStream 数据流的去向,而在 InputStreamRewinder 中又有对 InputStream 流的引用,那么在回到 InputStreamRewinder 之后我们来看看接下来又做了什么呢?

再次定位到 DecodeJob 类中的 runLoadPath 方法,在获取到 InputStreamRewinder 后,它会调用 path.load 方法并返回 Resource 资源类,这个 path 是在之前获取到的 LoadPath 对象,这里注意下,传递了一个 new DecodeCallback 对象的回调类,后面会用到。最后调用 InputStreamRewinder.cleanup() 进行资源释放。那么我们来看看 LoadPathload 方法是怎么实现的:

public class LoadPath<Data, ResourceType, Transcode> {...public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());try {return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);} finally {listPool.release(throwables);}}...
}

它直接调用 loadWithExceptionList 方法进行移交:

public class LoadPath<Data, ResourceType, Transcode> {...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 perffor (int i = 0, size = decodePaths.size(); i < size; i++) {DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);try {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;}...
}

loadWithExceptionList 方法中则会调用 decode 方法进行解码:

public class DecodePath<DataType, ResourceType, Transcode> {...public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);return transcoder.transcode(transformed, options);}@NonNullprivate Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width,int height, @NonNull Options options) throws GlideException {List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());try {return decodeResourceWithList(rewinder, width, height, options, exceptions);} finally {listPool.release(exceptions);}}...
}

decode 方法中调用 decodeResource 方法,然后在调用 decodeResourceWithList 方法真正的开始解码:

public class DecodePath<DataType, ResourceType, Transcode> {...private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {Resource<ResourceType> result = null;//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = decoders.size(); i < size; i++) {ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);try {DataType data = rewinder.rewindAndGet();if (decoder.handles(data, options)) {data = rewinder.rewindAndGet();result = decoder.decode(data, width, height, options);}// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but// instead log and continue. See #2406 for an example.} catch (IOException | RuntimeException | OutOfMemoryError e) {if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Failed to decode data for " + decoder, e);}exceptions.add(e);}if (result != null) {break;}}if (result == null) {throw new GlideException(failureMessage, new ArrayList<>(exceptions));}return result;}...
}

这里首先遍历 decoders 集合,分别的获取到 ResourceDecoder 解码器,还记得我们的 decoders 都包含哪些解码器吗?

没错主要包含两种:BitmapDrawable.classGifDrawable.class

不记得的往上面翻下,上面已详细的讲解过了。

然后通过 rewinder.rewindAndGet() 获取我们的 InputStream 数据流:

public final class InputStreamRewinder implements DataRewinder<InputStream> {// 5mb.private static final int MARK_LIMIT = 5 * 1024 * 1024;private final RecyclableBufferedInputStream bufferedStream;InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);bufferedStream.mark(MARK_LIMIT);}public InputStream rewindAndGet() throws IOException {bufferedStream.reset();return bufferedStream;}...
}

通过 decoder.handles(data, options) 方法来过滤掉不相匹配的解码器:

public class Glide implements ComponentCallbacks2 {...Glide(@NonNull Context context,@NonNull Engine engine,@NonNull MemoryCache memoryCache,@NonNull BitmapPool bitmapPool,@NonNull ArrayPool arrayPool,@NonNull RequestManagerRetriever requestManagerRetriever,@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,int logLevel,@NonNull RequestOptions defaultRequestOptions,@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {registry....append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)....append(Registry.BUCKET_BITMAP_DRAWABLE,InputStream.class,BitmapDrawable.class,new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))....append(Registry.BUCKET_GIF,InputStream.class,GifDrawable.class,new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))...}...
}

Bitmap.class 已被我们过滤掉,剩下的就只有 BitmapDrawable.classGifDrawable.class ,当我们调用 handles 方法进行匹配时, StreamGifDecoder 解码器是怎么处理的:

public class StreamGifDecoder implements ResourceDecoder<InputStream, GifDrawable> {...public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException {return !options.get(GifOptions.DISABLE_ANIMATION)&& ImageHeaderParserUtils.getType(parsers, source, byteArrayPool) == ImageType.GIF;}...
}

它主要是针对 options 属性为gif的图片来解码的,其实这不用我说大家也是知道的,而我们加载的只是普通的静态图片,因此它是不符合我们的匹配规则的,在来看 BitmapDrawableDecoder 中,它传入的是 StreamBitmapDecoder 的解码器:

public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {...public boolean handles(@NonNull InputStream source, @NonNull Options options) {return downsampler.handles(source);}...
}

downsampler.handles(source); 中只要 sourceInputStream 就返回 true ,因此匹配成功。

那么由上分析,我们真正的 decoder 就是 BitmapDrawableDecoder

回到 decodeResourceWithList 方法中,获取到真正的 decoder 解码器,将会调用 decode 方法正式解码:

public class BitmapDrawableDecoder<DataType> implements ResourceDecoder<DataType, BitmapDrawable> {...public boolean handles(@NonNull DataType source, @NonNull Options options) throws IOException {return decoder.handles(source, options);}@Overridepublic Resource<BitmapDrawable> decode(@NonNull DataType source, int width, int height,@NonNull Options options)throws IOException {Resource<Bitmap> bitmapResource = decoder.decode(source, width, height, options);return LazyBitmapDrawableResource.obtain(resources, bitmapResource);}...
}

它又会交给 StreamBitmapDecoderdecode 方法,然后在转移到 Downsampler 类中的 decode 方法中:

public final class Downsampler {...public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,Options options, DecodeCallbacks callbacks) throws IOException {Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"+ " mark()");byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();bitmapFactoryOptions.inTempStorage = bytesForOptions;DecodeFormat decodeFormat = options.get(DECODE_FORMAT);DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);boolean isHardwareConfigAllowed =options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);try {Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,requestedHeight, fixBitmapToRequestedDimensions, callbacks);return BitmapResource.obtain(result, bitmapPool);} finally {releaseOptions(bitmapFactoryOptions);byteArrayPool.put(bytesForOptions);}}...
}
public class BitmapResource implements Resource<Bitmap>,Initializable {...public static BitmapResource obtain(@Nullable Bitmap bitmap, @NonNull BitmapPool bitmapPool) {if (bitmap == null) {return null;} else {return new BitmapResource(bitmap, bitmapPool);}}...
}

这里就不再一个一个的看方法了,主要是在 DownsamplerdecodeFromWrappedStreams 方法中把 InputStream 数据流根据 widthheightoptions 给转化成 Bitmap 图片,然后把Bitmap存放到 BitmapResource 对象中去且直接的返回本身。

ok,获取到 BitmapResource 对象后,再次返回最初开始解码的 DecodePath 类中的 decode 方法中:

public class DecodePath<DataType, ResourceType, Transcode> {...public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);return transcoder.transcode(transformed, options);}...
}

从上面分析我们知道,我们解析的是普通的图片,所以这个 transcoder 就是 BitmapDrawableTranscoder 转换器类。(前面有详细的分析,忘记的可以向上翻看)

接着我们去 BitmapDrawableTranscoder 中的 transcode 方法看看:

public class BitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, BitmapDrawable> {...public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,@NonNull Options options) {return LazyBitmapDrawableResource.obtain(resources, toTranscode);}
}

transcode 方法中通过 toTranscode 获取 bitmap 图片,这个bitmap虽然经过多层的包装,它其实就是一个 BitmapResource 对象,这在上面我们有清楚的分析,只不过在返回的回调方法中有多次的封装而已。

public class BitmapResource implements Resource<Bitmap>,Initializable {...public static BitmapResource obtain(@Nullable Bitmap bitmap, @NonNull BitmapPool bitmapPool) {if (bitmap == null) {return null;} else {return new BitmapResource(bitmap, bitmapPool);}}public Bitmap get() {return bitmap;}...
}

获取到Bitmap图片后,调用 LazyBitmapDrawableResourceobtain 方法再次进行一次的封装:

public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,Initializable {...public static Resource<BitmapDrawable> obtain(@NonNull Resources resources, @Nullable Resource<Bitmap> bitmapResource) {if (bitmapResource == null) {return null;}return new LazyBitmapDrawableResource(resources, bitmapResource);}private LazyBitmapDrawableResource(@NonNull Resources resources,@NonNull Resource<Bitmap> bitmapResource) {this.resources = Preconditions.checkNotNull(resources);this.bitmapResource = Preconditions.checkNotNull(bitmapResource);}...
}

Bitmap 封装到 LazyBitmapDrawableResource 对象中进行返回。

ok,到这里其实已完成了完完全全的解码和封装到。

一路把 LazyBitmapDrawableResource 对象返回到 DecodeJob 类中的 decodeFromRetrievedData 中,并赋值给 resource 变量:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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();}}...
}

resource 不为空时,将会调用 notifyEncodeAndRelease 方法并传递参数:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...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();}...
}

方法里面就不多解析了,它会调用 notifyComplete 方法:

class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,Runnable,Comparable<DecodeJob<?>>,Poolable {...private void notifyComplete(Resource<R> resource, DataSource dataSource) {setNotifiedOrThrow();callback.onResourceReady(resource, dataSource);}...
}

这里调用了 callback.onResourceReady(resource, dataSource); 方法,那这个callback是什么呢?

它其实是一个 ResourceCallback ,在 SingleRequest 中发起的,并且 SingleRequest 还实现了 ResourceCallback 接口内的方法:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {...public void onResourceReady(Resource<?> resource, DataSource dataSource) {stateVerifier.throwIfRecycled();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())) {releaseResource(resource);GlideException exception = new GlideException("Expected to receive an object of "+ transcodeClass + " but instead" + " got "+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "+ "Resource{" + resource + "}."+ (received != null ? "" : " " + "To indicate failure return a null Resource "+ "object, rather than a Resource object containing null data."));onLoadFailed(exception);return;}if (!canSetResource()) {releaseResource(resource);// We can't put the status to complete before asking canSetResource().status = Status.COMPLETE;return;}onResourceReady((Resource<R>) resource, (R) received, dataSource);}...
}

重点讲解下:在 onResourceReady 中使用 resource.get() ,我们知道这个 resource 就是 LazyBitmapDrawableResource 对象,来看看这个get获取到了什么:

public final class LazyBitmapDrawableResource implements Resource<BitmapDrawable>,Initializable {...public BitmapDrawable get() {return new BitmapDrawable(resources, bitmapResource.get());}...
}

在get中获取到了 BitmapDrawable 对象直接复制给了received变量,然后调用重载方法 onResourceReady 方法:

public final class SingleRequest<R> implements Request,SizeReadyCallback,ResourceCallback,FactoryPools.Poolable {...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;if (glideContext.getLogLevel() <= Log.DEBUG) {Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "+ LogTime.getElapsedMillis(startTime) + " ms");}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();}...
}

onResourceReady 方法中调用了 target.onResourceReady(result, animation); 还记得 target 是什么吗?

它在 load 方法中已讲解过,就是 DrawableImageViewTarget 对象,调用它的 onResourceReady 会转移到父类 ImageViewTarget 中:

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {...public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {if (transition == null || !transition.transition(resource, this)) {setResourceInternal(resource);} else {maybeUpdateAnimatable(resource);}}...
}

然后在调用 setResourceInternal 方法:

public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>implements Transition.ViewAdapter {...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);}protected abstract void setResource(@Nullable Z resource);
}

setResource 是抽象方法,由它的子类实现,我们在回到 DrawableImageViewTarget 中:

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {public DrawableImageViewTarget(ImageView view) {super(view);}public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {super(view, waitForLayout);}protected void setResource(@Nullable Drawable resource) {view.setImageDrawable(resource);}
}

到这里直接调用 view 进行设置图片了,这个view就是我们 Imageview 了,所以到这里就设置好了加载的图片了。

说实在的 Glide 的源码非常的复杂,往往深入进去就无法出来了,相信这篇博客很能好的给大家一个参考,能让大家对整个Glide有个全面的理解。




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2014年考研英语一翻译知识点

题目讲解网址 总结 1.做翻译题,不用看句子前后的地方,直接看要翻译的部分 2.多根据语境去翻译 3.如果是不认识的单词,一般都是我们平常经常使用/说的词的代替高级词 题目句子 It is also the reason why when we try to describe music with words, all wecan do is articul…

计算机操作系统 死锁问题

概念 条件是基础&#xff0c;在一定的原因下&#xff0c;产生结果死锁三胞胎 死锁 僵持&#xff0c;消耗时间&#xff0c;双方都占用了部分资源&#xff0c;不释放活锁 双方互相谦让&#xff0c;都不占用资源饥饿 谦让的一方一直等待&#xff0c;无法占有资源&#xff0c;导致…

武忠祥.高等数学.基础课-第一章函数 极限 连续P10

sin(1/x) 详细解析网址 1.图像 2.极限 x–>0时,函数极限不存在 sin2x 详细作图网址 1.图像 2.周期为Π f(x)周期为T,f(axb)周期为T/|a| 所以sinx周期为2Π,sin2x周期为2Π/2Π |sinx| 详细讲解网址 1.图像 2.周期:Π 3.绝对值 &#xff08;1&#xff09;y|sinx|的图…

算法章节 递归、排序、⼆分查找

递归 概念与特性函数调⽤函数⾃身的编程⽅式叫做递归&#xff0c;调⽤为”递“&#xff0c;返回为”归“三个条件1. ⼀个问题的解可以分解为多个⼦问题的解&#xff1b; 2. 分解之后的⼦问题&#xff0c;除了数据规模不同&#xff0c;求解思路跟原问题相同&#xff1b; 3. 存在…

codeforces 50A-C语言解题报告

50A题目网址 解题报告-others 题目解析 1.输入n x m大小的木板,使用21大小的多米诺去填满,求最多的多米诺数目 2.通过分析把木板分为奇数和偶数的情况 1)有一边是偶数的情况: 使用2去填满 2)两个边都是奇数 奇数-1偶数 还是让木板的(奇数-1)边去和2平行,再加上 (m-1)/2(n/1)…

Java命令:jps — 查看进程信息

文章目录一、简介二、常用命令1、jps2、jps -l3、jps -q4、jps -m5、jps -v6、jps失效一、简介 JVM Process Status Tool&#xff0c;显示指定系统内所有的HotSpot虚拟机进程。 功能&#xff1a; 显示当前所有java进程pid的命令&#xff0c;我们可以通过这个命令来查看到底启…

操作系统概述 记录操作系统相关知识

操作系统 现代计算机系统由一个或多个处理器、主存、打印机、键盘、鼠标、显示器、网络接口以及各种输入/输出设备构成。上面提到的这些东西都属于硬件资源&#xff0c;用户不会直接和硬件进行交互&#xff0c;计算机安装了一层软件&#xff0c;这层软件能够通过响应用户输入的…

JDK工具使用大全

文章目录一、简介一、简介 在JDK的bin目录下有很多命令行工具&#xff1a; 常用工具使用详解如下&#xff1a; Java命令&#xff1a;jps — 查看进程信息 Java命令&#xff1a;jstack — 获取线程dump信息 Java命令&#xff1a;jmap — 打印指定进程的共享对象内存映射或…

Linux进程 excel族函数的用法

介绍 使用fork创建一个进程之后&#xff0c;经常会在新进程中调用exec函数执行别的程序当前进程调用exec函数之后&#xff0c;这个进程会被完全替代换成新的程序&#xff0c;即便如此仍然是同一个进程&#xff0c;进程ID不变函数族 execl execlp execle execvp execvpe头文件 …

C++primer 12章 动态内存和智能指针

C引入智能指针的目的 使用智能指针来管理动态分配的对象&#xff0c;当一个对象应该被释放的时候&#xff0c;指向他的智能指针确保自动释放它 内存分配 静态内存&#xff1a;局部static对象、类static数据成员、定义在任何函数之外的变量栈内存&#xff1a;定义在函数内的非…

Mac下iTerm2的安装与配置

目录一、iTerm2简介二、下载以及安装三、iTerm2主题配置四、配置Oh My Zsh1、安装方式&#xff08;1&#xff09;一键安装&#xff08;2&#xff09;手动安装3、切换zsh4、修改主题五、配置Meslo字体六、声明高亮七、自动建议填充八、iTerm2快速隐藏和显示九、iTerm2隐藏用户名…

Java命令:jinfo — 查看进程参数

目录一、简介二、常用命令1、jinfo -flags pid : 打印当前指定java进程中已经设定的所有JVM参数信息2、jinfo -flag pid : 打印指定名称的参数3、jinfo -flag [|-] pid : 打开或关闭参数4、jinfo -sysprops pid : 打印当前java进程中设定的系统环境参数一、简介 jinfo 是 JDK …

C++primer第八章 IO库 8.1 IO类

IO库设施 istream &#xff08;输入流&#xff09;类型&#xff0c;提供输入操作。ostream &#xff08;输出流&#xff09;类型&#xff0c;提供输出操作。cin,—个 istream对象&#xff0c;从标准输入读取数据。cout, 一个ostream对象&#xff0c;向标准输出写入数据。cerr…

2014年英语一作文partB漫画作文

题目 Write an essay of 160-200 words based on the following drawing.In your essay you should describe the drawing brieflyexplain its intended meaning,give your comments 做题点 1.使用三段式,第一段:图片内容;第二段:图片暗示;第三段:写自己的评论 2.描述图片…

Spring Cloud 系列之 Nacos 配置中心

目录一、Nacos简介二、Nacos安装及配置1、环境准备2、安装包下载&#xff08;1&#xff09;源码方式&#xff08;2&#xff09;发行包方式3、启动Nacos服务4、Nacos数据库配置&#xff08;1&#xff09;MySQL数据源&#xff08;2&#xff09;初始化 MySQL 数据库&#xff08;3&…

C++primer第八章 IO库 8.2 文件输入输出

8.2文件输入输出 头文件fstream定义了三个类型来支持文件IO&#xff1a;ifstream从一个给定文件读取数据&#xff0c;ofstream向一个给定文件写入数据&#xff0c;以及fstream可以读写给定文件。在17.5.3节中&#xff08;第676页&#xff09;我们将介绍如何对同一个文件流既读…

SpringBoot 集成 Nacos

目录一、前言二、Nacos集成1、引入Nacos依赖2、设置Nacos配置3、加载Nacos配置中心配置项4、Nacos集成验证5、Nacos配置中心配置项动态生效Nacos安装详见&#xff1a;Spring Cloud 系列之 Nacos 配置中心 一、前言 上一篇已经讲解了怎样安装安装、启动、配置 Nacos&#xff0c…

C++primer第八章 IO库 8.3string流

8.3string流 sstream头文件定义了三个类型来支持内存IO,这些类型可以向string写入数据,从string读取数据&#xff0c;就像string是一个IO流一样。istringstream从string读取数据&#xff0c;ostringstream向string写入数据&#xff0c;而头文件stringstream既可从string读数据…

英语口语海报演讲--东软

海报 海报上的内容 Nuclear waste water 1.Damage the devastating impact of nuclear radiation on the world 2.Marine life genetically mutated or dead 3.water resources polluted water resources 4.the future of humanity genetic damage/food and environment destr…

C++primer第九章 顺序容器 9.1 顺序容器概述 9.2容器库概览

一个容器就是一些特定类型对象的集合。顺序容器(sequentialcontainer)为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值&#xff0c;而是与元素加入容器时的位置相对应。与之相对的&#xff0c;我们将在第11章介绍的有序和无序关联容器&#xff0c;则根据…