作为Android平台使用最广泛的图片加载库,Glide具有快速高效、API易于使用、支持扩展等特点。
Glide基本用法
Glide使用简明的流式语法API,通过一行代码就可以进行图片加载。本文也将从源码角度,分析with()、load()和into()三个函数的内部原理。
Glide.with(this).load(url).into(imageView)
三步流程解析
with()
绑定不同的生命周期
with()用来执行对Glide的初始化,以及绑定生命周期。它可以接收多种参数:
Context:将Glide与Application的生命周期绑定,意味着即使推出了正在加载图片的页面,加载任务仍然不会停止。不建议这么做。Activity/FragmentActivity/Fragment:与相应页面绑定,当页面onPause()时,暂停请求,onStart()时恢复请求,onDestroy()时清空请求。View:会使用View.getContext()得到的Activity/Fragment Context进行绑定,但不建议这么用,因为View未发生attach()时是没有Context的;同时,对于具有复杂hierarchy的视图结构,开销很大。
所有的with()函数都会走到getRetriever()中。
Glide.java
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
创建Glide单例
retriever翻译为“猎犬、寻回器”,getRetriever()用来获取(新建or复用)一个RequestManager,它首先断言传入的Context非空,然后调用Glide.get().getRequestRetriever()。
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
Glide.get()采用饿汉式单例模式,进行初始化,在APP生命周期内,使用同一个Glide单例。
单例背景知识:饱汉——等到调用时才初始化,饿汉——类加载即进行初始化
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
初始化函数较长,已经把关键逻辑在注释中进行说明。
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中的自定义Module
manifestModules = new ManifestParser(applicationContext).parse();
}
// 使用@excludes注解解决自定义module冲突,因为一个APP进程只允许拥有一个module
if (annotationGeneratedModule != null
&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
Set<Class<?>> excludedModuleClasses =
annotationGeneratedModule.getExcludedModuleClasses();
Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
while (iterator.hasNext()) {
com.bumptech.glide.module.GlideModule current = iterator.next();
if (!excludedModuleClasses.contains(current.getClass())) {
continue;
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
}
iterator.remove();
}
}
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
builder.setRequestManagerFactory(factory);
// <--重点2:加载module中的自定义配置
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
自定义AppGlideModule
APP可以自定义AppGlideModule,有两种方式:
- 4.0之前,在
AndroidManifest.xml文件里面定义 - 4.0及以后,通过
@GlideModule注解定义
public abstract class AppGlideModule extends LibraryGlideModule implements AppliesOptions {
public boolean isManifestParsingEnabled() {
return true;
}
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
// Default empty impl.
}
}
通过自定义applyOptions()函数,可以修改Glide默认的配置,比如Bitmap缓存池大小、调整Executor等。
Module冲突
由于Glide限制在一个APP进程内只允许有一个自定义AppGlideModule存在,如果引入的第三方库也定义了Module,就会发生冲突。此时使用@excluded注解来使其中一个无效。
例如,你想让第三方库定义的com.example.unwanted.GlideModule失效,使自己定义的MyAppGlideModule生效,可以这么写。
@Excludes(com.example.unwanted.GlideModule)
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }
也可以一次排除多个模块。
@Excludes({com.example.unwanted.GlideModule, com.example.conflicing.GlideModule})
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }
getRetriever()
回到with()函数,完成Glide初始化后,调用getRequestManagerRetriever(),得到new出来的RequestManagerRetriever实例,接着调用get()获得请求管理器Retriever,这部分逻辑还挺有意思的,一起来看下。
RequestManagerRetriever.java
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
// 主线程,继续调用对应的get函数
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
// 子线程,返回ApplicationManager
return getApplicationManager(context);
}
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity); // 断言Activity未被销毁
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet( // <--重点:创建空Fragment
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
// 1.使用ApplicationContext,2.在后台线程,这两种场景下会使用ApplicationManager
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
Glide glide = Glide.get(context.getApplicationContext()); // get到之前创建的单例
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager; //
}
【重点】空Fragment策略
Glide是如何把图片任务与Activity生命周期进行绑定的,以便于Activity停止时,中止图片加载,Activity销毁时,清空加载任务?答案就是,它创建了一个空Fragment,并通过宿主Activity的FragmentManager,将其加入到当前Activity中。
这样,当Activity执行onDestroy()时,会对其内部的Fragment分发destroy事件,从而通知到Glide创建的空Fragment。
RequestManagerRetriever.java
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 得到传入Activity对应的空Fragment
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// TAG="com.bumptech.glide.manager"
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment(); // 创建空Fragment
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart(); // 如果宿主Activity可见,则触发lifecycle回调的onStart()
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
ActivityFragmentLifecycle是用于监听Activity、Fragment生命周期的对象,它的实现很简单,有三个方法:
onStart():恢复加载任务,在onStart时触发onStop():暂停加载任务,在onStop时触发,注意这里暂停后是可以恢复的onDestroy():终止任务并清空状态,无法再恢复
我认为这里也可以简单点直接使用registerActivityLifecycleCallback来处理
小结
以上完成了对Glide.with()过程的分析,小结一下,共包含以下几个主要步骤:
- 判断传入的Context非空、未被Destory
- 创建Glide单例(首次)或者获得已创建的单例(非首次)
- (首次)初始化Glide单例,通过反射(>=4.0)或者manifest文件(<4.0)加载自定义的AppGlideModule,读取其Options
- 如果过传入的context是Application Context,或者在子线程调用,则得到一个绑定了Application生命周期的RequestManager
- 如果传入的是Activity、Fragment,且在主线程调用,则得到绑定了Activity生命周期的RequestManager
- 内部通过创建一个空Fragment来实现生命周期绑定功能
load()
多种图片来源
with()得到RequestManager后,对其调用load()函数,load()支持加载多种类型的图片来源(如下图),下文取最典型的url场景进行分析。
load(url)会声明以Drawable形式进行加载,asDrawable()创建一个RequestBuilder,从名字上可以看出使用的是构造器模式。
RequestManager
RequestManager.java
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);
}
RequestBuilder
负责设置单次请求的Option并且构建出请求对象,它的构造函数如下,会保存传入的glide、requestManager、transcodeClass(目标解码类,这里是Drawable)、context,并且应用RequestManager的设置。
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
// 将RequestManager中定义的listener、option设置进RequestBuilder
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
创建完RequestBuilder对象后,对其调用load(Object),它会调用自身的loadGeneric()函数,将入参(url)设置给model成员变量,并返回自身。model可以理解为你要加载的这个东西,无论是url、Bitmap还是Drawable都可以作为model。
注意,对于传入String类型url的情况,会把url作为缓存Key使用。
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
小结
以上就是load()函数的过程,对于加载图片url的场景,会以Drawable形式进行加载,构建出RequestBuilder对象后,设置RequestManager中定义的options和listener,随后返回RequestBuilder对象。
into()
上一步得到初始化RequestBuilder后,对其调用into(ImageView)以完成图片加载,这是Glide加载过程中最复杂的部分,我将详细对此进行分析。首先看into()函数本身。
三项基本职责
into()的目的是向ImageView中加载图片,根据目标ImageView的状态,它有3个职责。
- 对构建好的RequestBuilder,执行加载流程
- 如果目标ImageView已经有了加载任务正在执行中,取消之
- 释放目标ImageView中曾经加载过的资源
ReqeustBuilder.java
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread(); // 断言处于主线程,只能在主线程进行into调用
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
// 设置Transformation,如圆角、居中裁切等,类似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()) { // 使用xml中声明的scaleType
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), // 构建Target
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
into()函数首先进行主线程判断,之所以这么做的原因是,在后续完成图片资源加载后需要设置给ImageView,Android系统要求只能在主线程操作View。随后设置Transformations变换,然后调用同名into()函数。
在调用同名into()函数时,使用的第一个参数是glideContext.buildImageViewTarget(view, transcodeClass),这里transcodeClass是Drawable.class,这一行代码的含义是构建将Drawable显示在ImageView中的Target对象,该Target对象负责将加载完成的Drawable与ImageView关联(其实就是显示在ImageView中)通过这种方式隐藏了Target和transcodeClass的具体实现,达到解耦的目的,可以将Bitmap或者Drawable显示在ImageView里面。
继续看另一个into()函数。
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) { // model是我们图片的url地址
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
// Step 1 组装Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
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); // Step 2 清空target当前任务
target.setRequest(request); // Step 3 将Request绑定至target
requestManager.track(target, request); // Step 4 执行request
return target;
}
into()的4个步骤
主要有4个步骤,我们逐步分析。
Step.1 组装Request
调用buildRequest()最终会调用到buildRequestRecursive(),它会组装2个请求,分别是主图请求(mainRequest)和error占位图请求(errorRequest,构建请求时设置error()触发)。
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
Request mainRequest =
buildThumbnailRequestRecursive( // Request 1 主图片请求,包含缩略图
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest =
errorBuilder.buildRequestRecursive( // Request 2(可选)error占位图
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
buildThumbnailRequestRecursive & obtainRequest
构建主图请求的函数名叫buildThumbnailRequestRecursive(),这里其实容易引起误解,明明是主图的请求,为什么叫做“缩略图”请求呢?这是因为在设置RequestBuilder时,可以通过thumbnail()函数设置size更小的占位图,当占位图请求先返回时,会优先展示占位图,随后替换为更大的主图展示,从而减少用户等待时间。逻辑最终会落入obtainRequest()函数。
obtainRequest()
RequestBuilder.java
private Request obtainRequest(
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,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
SingleRequest.obtain(),从名字可以看出这是一个运用了对象池的生成器,类似于Android中Message.obtain()。果不其然,取出/新建Request后调用init(),内容也不复杂,无非是设置一大堆变量,需要注意最后一行,将status设置成PENDING状态。
SingleRequest.java
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> 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,
Executor callbackExecutor) {
@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,
callbackExecutor);
return request;
}
private synchronized void init(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> 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,
Executor callbackExecutor) {
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
this.requestOptions = requestOptions;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.priority = priority;
this.target = target;
this.targetListener = targetListener;
this.requestListeners = requestListeners;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
this.callbackExecutor = callbackExecutor;
status = Status.PENDING; // <--重点:状态为pending
if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
requestOrigin = new RuntimeException("Glide request origin trace");
}
}
Step.2 清空target当前任务
取消当前ImageView上正在Pending的任务,同时将ImageView上已经显示的资源(如Bitmap)释放掉,这里不继续深究了,只需要知道untrack(Target)会清理掉目标Target所关联的请求即可。
public synchronized void clear(@Nullable final Target<?> target) {
if (target == null) {
return;
}
untrackOrDelegate(target);
}
private void untrackOrDelegate(@NonNull Target<?> target) {
boolean isOwnedByUs = untrack(target);
if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
Request request = target.getRequest();
target.setRequest(null);
request.clear();
}
}
Step.3 将Request绑定至target
target.setRequest(request),对于ImageView,会将Reqeust对象设置为它的Tag,如果有设置多个Tag的需求还需要传入tagId参数,默认是没有tagId的。
Step.4 执行Request
到这里,已经把ImageView与Request通过Tag进行关联,接下来就是执行请求的过程。
RequestManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target); // 将target放入Set集合中
requestTracker.runRequest(request); // 真正发起请求
}
runRequest()
boolean类型的isPaused变量默认是false,从而调用request.begin()。
RequestTracker.java
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);
}
}
onSizeReady()
begin()是一个接口方法,其实现位于SingleRequest.java,抛去无关主流程的判断,它会调用onSizeReady()函数,启动Engine进行加载。
SingleRequest.java
@Override
public synchronized void begin() {
assertNotCallingCallbacks(); // 阻止在error时手动发起Request
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
...
// 准备开始请求
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()); // 通知开始加载,无特殊逻辑,不需要关心
}
...
}
Engine对象是在Glide构造函数里就已经初始化的,在调用engine.load()时,传入的倒数第二个参数是ResourceCallback类型的回调,也就是SingleRequest对象自身,它实现了onResourceReady()和onLoadFailed()两个方法。
SingleRequest.java
@Override
public synchronized void onSizeReady(int width, int height) {
...
if (status != Status.WAITING_FOR_SIZE) { // 必须在status=WAITING_FOR_SIZE时才能进入
return;
}
status = Status.RUNNING; // 切换status为RUNNING
// 是否需要放大显示
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
...
}
Engine.load()执行流程
engine.load()函数的执行流程如下:
- 根据传入的参数,生成唯一
Key,该Key用于判断请求是否雷同 - 先检索
ActiveResources活动缓存,这是当前正在其它View中显示的图像,如果发现是相同的请求,则直接获取其加载好的资源。这里用到的是内存缓存Memory Cache,但没有容量上限(因为正在其它ImageView显示中) - 然后检索真正的
Memory Cache,它是在Glide对象初始化时设置的,大小为屏幕长*宽*4(字节)*2(屏幕数),类型是LruCache,实现得很精妙 - 如果
Memory Cache命中失败,就看当前正在执行的job里面,有没有与key相同的job。若找到了就把新的callback赋给旧的job并返回,若没找到则继续执行 - 创建
engineJob,它用于封装加载结果的回调,如加载成功/失败。接着创建了decodeJob,并把engineJob传入给decodeJob - 调用
engineJob.start(decodeJob),启动任务
Engine.java
public synchronized <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 生成key用于集合及比对
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> 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, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
Memory Cache 的 LRU 实现
这个类实现得非常之优雅,按照字节容量大小来判断是否需要移除最少使用的,而不是简单的按元素个数判断。
public class LruCache<T, Y> {
private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);
private final long initialMaxSize;
private long maxSize;
private long currentSize;
public LruCache(long size) {
this.initialMaxSize = size;
this.maxSize = size;
}
// 元素的“大小”,对于图片类型可以重载,以返回其字节数
protected int getSize(@Nullable Y item) {
return 1;
}
protected synchronized int getCount() {
return cache.size();
}
// LinkedHashMap是队列,先进先出
@Nullable
public synchronized Y put(@NonNull T key, @Nullable Y item) {
final int itemSize = getSize(item);
if (itemSize >= maxSize) {
onItemEvicted(key, item); // 如果新item直接超出容量则移它
return null;
}
if (item != null) {
currentSize += itemSize;
}
@Nullable final Y old = cache.put(key, item);
if (old != null) {
currentSize -= getSize(old); // key相同,替换掉old,需要减掉它的size
if (!old.equals(item)) {
onItemEvicted(key, old); // 触发移除回调
}
}
evict(); // 判断size
return old;
}
// 不断移除最久使用的元素,以便size不超过max
protected synchronized void trimToSize(long size) {
Map.Entry<T, Y> last;
Iterator<Map.Entry<T, Y>> cacheIterator;
while (currentSize > size) {
cacheIterator = cache.entrySet().iterator(); // 因为是有序队列,所以直接取第一个就是最早插入的
last = cacheIterator.next();
final Y toRemove = last.getValue();
currentSize -= getSize(toRemove);
final T key = last.getKey();
cacheIterator.remove();
onItemEvicted(key, toRemove);
}
}
private void evict() {
trimToSize(maxSize);
}
}
EngineJob.start()
先判断是否使用磁盘缓存,默认是使用的。Executor含义是执行器,它会用来执行decodeJob,其实就是调用Job.run()函数,因为Job本身是一个Runnable对象。
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
磁盘缓存DiskCache
diskCacheExecutor是在GlideBuilder中进行初始化的,其实现位于GlideExecutor,其实不仅是diskCacheExecutor,另外3个SourceExecutor, UnlimitedSourceExecutor和AnimationExecutor同样是在这里进行初始化。
它们在线程数、保活时间上有所区别。
GlideExecutor.java
public static GlideExecutor newDiskCacheExecutor() {
return newDiskCacheExecutor(
DEFAULT_DISK_CACHE_EXECUTOR_THREADS, // 1条线程
DEFAULT_DISK_CACHE_EXECUTOR_NAME,
UncaughtThrowableStrategy.DEFAULT);
}
public static GlideExecutor newDiskCacheExecutor(
int threadCount, String name, UncaughtThrowableStrategy uncaughtThrowableStrategy) {
return new GlideExecutor(
new ThreadPoolExecutor(
threadCount /* corePoolSize */,
threadCount /* maximumPoolSize */,
0 /* keepAliveTime */, // 不保活,用完即扔
TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(),
new DefaultThreadFactory(name, uncaughtThrowableStrategy, true)));
}
通过executor.execute()方法,调用到DecodeJob.run()。
decodeJob.run()
run()内部调用runWrapped(),首次进入时,runReason=INITIALIZE,getNextState()返回State.RESOURCE_CACHE。
private void runWrapped() {
switch (runReason) {
case INITIALIZE: // 首次进入时
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource() // 默认true
? 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);
}
}
数据来源的分类
Stage是枚举类,表明当前要decode的数据来源:
RESOURCE_CACHE:在将图片保存为显示大小的磁盘缓存查找DATA_CACHE:在将图片保存为原始大小的磁盘缓存查找SOURCE:从数据真实来源(如网络)读取
DecodeJob.java
/**
* 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,
}
Generator、Loader与Fetcher
对于不同的stage,getNextGenerator()返回相应的Generator进行处理。其中ResourceCache、DataCache是从磁盘缓存查找,如果没有命中,则调用SourceGenerator从数据源头获取。
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);
}
}
每一个Generator都实现了startNext()函数,在Glide初始化时会注册一系列Loader,其中就有获取网络图片的HttpGlideUrlLoader,它使用HttpUrlFetcher获取数据。
SourceGenerator.java
@Override
public boolean startNext() {
if (dataToCache != null) { // <--把上次成功获取到的数据进行缓存
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
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); // <--从网络加载图片InputStream
}
}
return started;
}
HttpUrlFetcher从网络加载图片字节流。
HttpUrlFetcher.java
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
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));
}
}
}
Generator包装了Loader,Loader包装了Fetcher。
处理顺序总结如下,其中磁盘缓存可以在Options中设置以跳过。
INITIALIZE:初始化状态,直接向下执行RESOURCE_CACHE:位于磁盘的,转换后图片缓存,大小为View大小。转换Transformation如剪裁、应用过滤器等DATA_CACHE:位于磁盘的,原始图片缓存SOURCE:从数据来源获取,如服务器FINISHED
decodeFromRetrievedData()
获取到资源后,解码成Bitmap或者Gif,最后通过回调交给ImageView进行展示。