前言
Glide 源码解析系列到目前为止刚好写了10篇,而本篇做为收篇之作终于要完成了。一开始决定写这个系列是因为面试被问到源码比较多,虽然也看过别人写的博客,但是大多数都是流程走一遍,很多细节的东西并没有掌握,一被问到就懵逼。我们做技术的不像其他行业,不懂就是真的不懂,想吹也没法吹。所以还是决定亲自撸一遍源码,不仅为了面试,而且对自己能力的提升也有好处。
开始加载
在 Glide源码解析之RequestBuilder 中我们讲了 SingleRequest 的创建,并且当生命周期为 onStart() 的时候会调用 begin() 来开始执行加载请求。
在 begin() 里首先会对当前的状态进行检查,然后判断宽高是否有设置或者能获取到。由于 ImageView 是需要经过 onLayout() 后才能确定自身宽高的,所以在这里我们先要去获取到 ImageView 的宽高。
public synchronized void begin() {
//防止同时执行
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
//如果已经完成的则直接处理
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
//等待能获取到 ImageView 的宽高
status = Status.WAITING_FOR_SIZE;
// overrideWidth 和 overrideHeight 默认为-1,所以这里为 false
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()); //设置占位图
}
}
获取宽高
由于 View 的宽高是要在 onLayout() 之后才能确定下来,而 onDraw() 是在 onLayout() 之后执行的,所以这里通过向 ViewTreeObserver 添加 OnPreDrawListener ,这样在 onDraw() 之前就能获取到宽高。
//ViewTarget
public void getSize(@NonNull SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
//SizeDeterminer
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this); //关注点
observer.addOnPreDrawListener(layoutListener); //执行点
}
}
//SizeDeterminerLayoutListener
public boolean onPreDraw() {
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens(); //执行点
}
return true;
}
//SizeDeterminer
void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
return;
}
notifyCbs(currentWidth, currentHeight); //执行点
clearCallbacksAndListener(); //移除监听
}
private void notifyCbs(int width, int height) {
for (SizeReadyCallback cb : new ArrayList<>(cbs)) {
cb.onSizeReady(width, height); //回调给 SingleRequest
}
}
启动加载
在获取到宽高之后就将加载的工作交给 Engine 来完成,它首先会去 ActiveResources 中获取缓存,对 ActiveResources 不了解的可以看下 Glide源码解析之ActiveResources。
如果 ActiveResources 没有缓存则接着去 MemoryCache 中获取缓存,对 MemoryCache 不了解的可以看下 Glide源码解析之MemoryCache。
如果没有内存缓存,则将获取数据的工作交给 DecodeJob 去执行。
PS:很多人都写着 Glide 有三级缓存,分别是内存、磁盘、网络。我估计很多人都没有亲自看过源码,看着当初第一个人这样写,结果都跟着写,误导了一大片人。Glide 的确有三级缓存,但是前两个都是内存缓存,就是 ActiveResources 和 MemoryCache ,最后一级是磁盘缓存 ,是 DiskCache 。
//SingleRequest
public synchronized void onSizeReady(int width, int height) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
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); //执行点
if (status != Status.RUNNING) {
loadStatus = null;
}
}
//Engine
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) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//获取 ActiveResources 缓存
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); //执行点
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//获取 MemoryCache 缓存
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); //执行点
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
// jobs 是缓存 EngineJob 的
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
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); //执行点
return new LoadStatus(cb, engineJob);
}
从 ActiveResources 获取缓存
这个没啥好说的,有就返回,没有就 null 。
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) { // isMemoryCacheable 默认为 true
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
从 MemoryCache 获取缓存
首先调用 remove() 获取缓存,这里之所以不调用 get() 是因为 ActiveResources 同样是内存缓存,它保存的是没被垃圾回收的资源。而 MemoryCache 是有内存大小限制的,将缓存存入 ActiveResources 后它也没必要再持有多一份。
如果有缓存的话则将资源包装成 EngineResource 返回。
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key); // 执行点
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached); // 将获取到的缓存存入 ActiveResources 中
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key); // 关注点
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource<?>) cached;
} else {
result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
}
return result;
}
执行加载
首先会获取线程池,由于默认的磁盘缓存策略是允许解码缓存资源的,所以获取到的是 diskCacheExecutor 。这是一个核心线程数和最大线程数为1,工作队列为 PriorityBlockingQueue 的线程池。Glide 默认的 Priority 为 NORMAL ,我们在使用的时候可以通过设置 Priority 来改变加载的优先级。
接着把 DecodeJob 放进线程池执行,因为它实现了 Runnable 接口。DecodeJob 的具体调用过程请查看 Glide源码解析之DecodeJob。
面试经常会被问到线程池,但是实际项目中却很少用到,因为大多数工作第三方库都帮我们做了,所以阅读源码的一个好处是能学到平时很少接触的知识。
//EngineJob
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
//DecodeJob
boolean willDecodeFromCache() {
Stage firstStage = getNextStage(Stage.INITIALIZE);
return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
//默认 diskCacheStrategy 的类为 AUTOMATIC ,decodeCachedResource() 返回true
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
获取数据完成
在 DecodeJob 获取到数据之后会通过 onResourceReady() 回调给 EngineJob ,首先对当前的状态进行检查,接着会将 resource 包装为一个 EngineResource ,这也不难解释为什么在上面 Engine 的 load() 里面从缓存获取的是 EngineResource 了。
然后对监听进行回调,这是由 Engine 实现的,在资源加载成功后对它进行缓存。
最后将 CallResourceReady 放进线程池执行。
//EngineJob
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult(); //执行点
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
listener.onEngineJobComplete(this, localKey, localResource); //执行点
//在 Engine 的 load() 中通过 engineJob.addCallback(cb, callbackExecutor); 添加进来
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb)); //执行点
}
decrementPendingCallbacks();
}
//Engine
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.activate(key, resource); //存进 ActiveResources 缓存
}
}
jobs.removeIfCurrent(key, engineJob);
}
回调数据
在上面我们知道会将 CallResourceReady 放进线程池执行,那么这个线程池是从哪来的呢。答案就是在一开始使用 into() 的时候进行赋值,一步步传递给 EnginJob的。这个线程池里面的 execute() 则是使用了主线程的 Handler 来进行 post() ,这样就把线程从子线程切换为主线程来执行了。
接着就会把资源通过回调传给 SingleRequest 了,它首先也会对资源进行检查,如果没有问题则最终会通过 onResourceReady() 来进行处理。
//RequestBuilder
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
public static Executor mainThreadExecutor() {
return MAIN_THREAD_EXECUTOR;
}
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
handler.post(command);
}
};
//CallResourceReady
public void run() {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
engineResource.acquire();
callCallbackOnResourceReady(cb); //执行点
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
//EngineJob
synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
try {
cb.onResourceReady(engineResource, dataSource); //执行点
} catch (Throwable t) {
throw new CallbackException(t);
}
}
//SingleRequest
public synchronized 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(); //获取实际的资源,这里是 BitmapDrawable
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);
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource); //执行点
}
加载图片
如果有设置 Listener 的,则会先调用 Listener 的回调,没有则会将资源交给 Target 去加载。需要注意的是如果你设置的 Listener 在 onResourceReady() 中返回了 true ,则 Glide 认为你已经自己处理了,不会再帮你把图片加载到 ImageView 里的。
ImageView 接受到资源后,最终会调用 ImageView 的 setImageDrawable() 将图片显示处理,到此整个加载流程就结束了。
private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
//这个 requestListeners 是使用时自己添加来监听加载成功与否的,比如
/*
* Glide.with(this)
* .load("")
* .listener(new RequestListener<Drawable>() {
* @Override
* public boolean onLoadFailed(@Nullable GlideException e, *Object model, Target<Drawable> target, boolean *isFirstResource) {
* return false;
* }
*
* @Override
* public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
* return false;
* }
* })
* .centerCrop()
* .into(imageView);*/
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
// 在 RequestBuilder 的 into() 中可知 targetListener 为 null
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource); // NO_ANIMATION
target.onResourceReady(result, animation); //执行点
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
//ImageViewTarget
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource); //执行点
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
setResource(resource); //执行点
maybeUpdateAnimatable(resource);
}
//DrawableImageViewTarget
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
总结
Glide 源码解析系列到此就完结了,仍然记得一开始看源码的时候随着调用链的越来越深,看的云里雾里的,曾经一度想放弃。但是后来想明白了,人生会经历的挫折还很多,一遇到困难就想放弃,那还怎么成大器。所幸的是自己坚持了下来,并且阅读源码的能力又更上一层楼。
总有一天,你会感谢那个不曾放弃的自己!