本篇作为Glide源码分析的开篇,以最简单的方式使用Glide,分析,最终图片是如何显示到ImageView上的。
Glide 版本:4.12.0
先说下结论:
加载顺序
-
Engine调用 loadFromActiveResources 方法 ,从
ActiveResources
加载。ActiveResources
内部是 有一个Map<Key, ResourceWeakReference>
, 以弱引用的方式持有ResourceWeakReference
。持有的资源在gc的时候,会被回收。 -
Engine 调用 loadFromCache,从内存缓存
MemoryCache cache
中加载。并把缓存中加载的资源 EngineResource 放入 activeResources,并增加 EngineResource 的引用计数acquired
,acquired
为0 的时候,会被重新从activeResources
移除。 如果资源可以缓存的话resource.isMemoryCacheable()
,那么就放入 memoryCache ,否则就 recycle 。 -
先尝试使用 ResourceCacheGenerator 从包含对原始数据应用了采样或者变换(downsampled/transformed)的磁盘缓存获取。
-
使用 DataCacheGenerator 从没有修改过的原始磁盘缓存文件获取。
-
使用 SourceGenerator 从网络下载原始数据。然后根据请求参数,对原始数据应用采样或者变换(downsampled/transformed)。根据磁盘缓存策略,原始数据可能首先被写入磁盘,或者原始数据应用采样或者变换(downsampled/transformed)以后,将变换以后的文件加入到磁盘缓存,将然后从缓存文件加载,而不是直接返回。
-
有了磁盘缓存以后,再次重试 3,4,5 步骤,这时候就加载到图片了,把加载到的 EngineResource 资源回调出去,最终是在 ImageViewTarget 的 onResourceReady 方法中为 ImageView 设置了res
setResource
。然后会在 Engine 的 onEngineJobComplete 方法中,把返回的 EngineResource 加入 activeResources。 -
再次加载同一张图片,activeResources 中中已经有缓存了,直接从 activeResources。 获取返回。
最简单的使用方式
/**
* 最简单的使用方式
*/
private void sourceCodeTest() {
Glide.with(this)
.load(Images.imageUrls[1])
.into(imageView1);
}
with 方法返回的是 RequestManager 对象。
RequestManager 的 load 方法。
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
RequestManager 的 asDrawable 方法。
@CheckResult
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 对象。
RequestBuilder 的 load方法。
@NonNull
private RequestBuilder <TranscodeType> loadGeneric(@Nullable Object model) {
if(isAutoCloneEnabled()) {
return clone().loadGeneric(model);
}
//model 就是 要加载的图片链接
this.model = model;
//将 isModelSet 置为true
isModelSet = true;
//最终返回了RequestBuilder本身,没有什么特殊的设置
return selfOrThrowIfLocked();
}
RequestBuilder 的 into 方法
@NonNull
public ViewTarget <ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions <?> requestOptions = this;
//注释1处,如果没有设置Transformation && 允许Transformation && view 的 ScaleType 不为null,则获取 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.
//在此方法中进行克隆,这样,如果我们使用此RequestBuilder加载到一个View中,然后加载到另一个目标中,
//则不会保留基于上一个视图的缩放类型应用的转换。
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.
}
}
//注释2处,调用重载的 into 方法
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/
null,
requestOptions,
Executors.mainThreadExecutor());
}
注释1处,如果没有设置Transformation && 允许Transformation && view 的 ScaleType 不为null,则获取 requestOptions 对象。
注释2处,调用重载的 into 方法。
RequestBuilder 的 into 方法。
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options, Executor callbackExecutor) {
//注释1处,在我们的例子中,target 是 DrawableImageViewTarget 对象。
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//注释2处,构建request,我们先忽略其中的细节
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
//注释3处,如果有和之前的请求相同的请求,并且不是跳过内存缓存的请求,则使用之前的请求。
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
//注释4处,取消Glide可能为此target的所有未完成的加载,并释放可能已为目标加载的任何资源(例如{@link Bitmap}),以便可以重用它们。
//注意:这里内部会清除Glide为Target设置的Tag。
requestManager.clear(target);
//注释5处,给target重新设置tag
target.setRequest(request);
//注释6处,跟踪请求
requestManager.track(target, request);
return target;
}
注释1处,在我们的例子中,target 是 DrawableImageViewTarget 对象。
注释2处,构建request,我们先忽略其中的细节。返回的是一个 SingleRequest 对象。
注释3处,如果有和之前的请求相同的请求,并且不是跳过内存缓存的请求,则使用之前的请求。我们先忽略这种情况。
注释4处,取消Glide可能为此 target 的所有未完成的加载,并释放可能已为目标加载的任何资源(例如{@link Bitmap}),以便可以重用它们。 注意:这里内部会清除Glide为Target设置的Tag。
RequestManager 的 clear 方法。这块细节先略过。分析Glide在RecyclerView中应用的时候, 再详细分析。
注释5处,清除tag以后,给target重新设置tag。这个是保证不乱序的原因。
ViewTarget 的setRequest方法。
@Override
public void setRequest(@Nullable Request request) {
setTag(request);
}
//tagId
private static int tagId = R.id.glide_custom_view_target_tag;
private void setTag(@Nullable Object tag) {
//清除的时候,会将Tag设置为null。
isTagUsedAtLeastOnce = true;
view.setTag(tagId, tag);
}
RequestBuilder 的 into 方法注释6处, RequestManager 调用 track 方法。
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
//调用 RequestTracker 的 runRequest 方法
requestTracker.runRequest(request);
}
RequestTracker 的 runRequest 方法。
public void runRequest(@NonNull Request request) {
requests.add(request);
if(!isPaused) {
//注释1处,调用 SingleRequest 的 begin 方法。
request.begin();
} else {
request.clear();
if(Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
注释1处,调用 SingleRequest 的 begin 方法。
@Override
public void begin() {
synchronized(requestLock) {
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, /* isLoadedFromAlternateCacheKey= */
false);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
//注释1处,开始状态是 WAITING_FOR_SIZE,等待控件测量过后有尺寸
status = Status.WAITING_FOR_SIZE;
if(Util.isValidDimensions(overrideWidth, overrideHeight)) {
//注释2处,如果指定了宽高,直接调用onSizeReady方法。
onSizeReady(overrideWidth, overrideHeight);
} else {
//注释3处,否则,调用Target的getSize方法。SingleRequest 本身做为回调传入
target.getSize(this);
}
if((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) &&
canNotifyStatusChanged()) {
//先给ImageView 设置placeholderDrawable
target.onLoadStarted(getPlaceholderDrawable());
}
if(IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(
startTime));
}
}
}
注释1处,开始状态是 WAITING_FOR_SIZE,等待控件测量过后有宽高信息。
注释2处,如果指定了宽高,直接调用onSizeReady方法。
注释3处,否则,调用Target的getSize方法。SingleRequest 本身做为回调传入。内部是调用 SizeDeterminer 的 getSize 方法。
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if(isViewStateAndSizeValid(currentWidth, currentHeight)) {
//如果有宽高信息了,直接回调 onSizeReady 方法
cb.onSizeReady(currentWidth, currentHeight);
return;
}
//没有宽高信息,叫回调cb添加到列表中,等待获取到宽高信息后,通知回调cb。
if(!cbs.contains(cb)) {
cbs.add(cb);
}
if(layoutListener == null) {
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
//注释1处,添加监听,监听尺寸的变化,在View绘制之前会执行这个layoutListener,此时已经有宽高信息了。
observer.addOnPreDrawListener(layoutListener);
}
}
注释1处,添加监听,监听尺寸的变化,在View绘制之前会执行这个layoutListener,此时已经有宽高信息了。
ViewTarget.SizeDeterminer.SizeDeterminerLayoutListener 类
private static final class SizeDeterminerLayoutListener
implements ViewTreeObserver.OnPreDrawListener {
private final WeakReference <SizeDeterminer> sizeDeterminerRef;
SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference <> (sizeDeterminer);
}
@Override
public boolean onPreDraw() {
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if(sizeDeterminer != null) {
//注释1处,调用 SizeDeterminer 的 checkCurrentDimens 方法。
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
注释1处,获取大调用 SizeDeterminer 的 checkCurrentDimens 方法。
@Synthetic
void checkCurrentDimens() {
if(cbs.isEmpty()) {
return;
}
//注释1处,注意,getTartgetWidth和getTargetHeight方法
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if(!isViewStateAndSizeValid(currentWidth, currentHeight)) {
//注释2处,宽高不合法,说明此时View还没有经过measure,直接返回,SizeDeterminerLayoutListener不会被清除。
//等待经过measure后,再次调用checkCurrentDimens方法。
return;
}
//注释3处,如果宽高合法通知回调,并清除监听SizeDeterminerLayoutListener。
notifyCbs(currentWidth, currentHeight);
clearCallbacksAndListener();
}
注释1处,注意,SizeDeterminer 的 getTargetWidth 和 getTargetHeight 方法。
private int getTargetWidth() {
</