Glide源码学习笔记二:load方法做了什么?
文章目录
前言
上文分析完Glide的第一步with(Context context),链接在此:Glide源码学习笔记一:with方法做了什么?,Glide在图片加载的界面添加了一个看不见的RequestManagerFragment,返回了一个与之绑定的RequestManager,接下来分析load(url)做的事情。
RequestManager
首先搞清楚RequestManager是做什么的,来看看类注释:
/**
* A class for managing and starting requests for Glide. Can use activity, fragment and connectivity
* lifecycle events to intelligently stop, start, and restart requests. Retrieve either by
* instantiating a new object, or to take advantage built in Activity and Fragment lifecycle
* handling, use the static Glide.load methods with your Fragment or Activity.
*/
简单翻译一下就是作为管理以及开始Glide请求的一个类。连接了activity,fragment的生命周期,可以更加灵活的停止,开始,重试图片加载请求。上一篇文章说的就是如何赋予RequestManager这样的能力。那RequestManager是如何实现这些功能的呢?
/**
* Cancels any in progress loads, but does not clear resources of completed loads.
* 取消当前RequestManager的加载请求但是不清除已经下载完成的资源
*/
public synchronized void pauseRequests() {
requestTracker.pauseRequests();
}
/**
* Cancels any in progress loads and clears resources of completed loads.
* 取消当前RequestManager的加载请求并且清除已经下载完成的资源
*/
public synchronized void pauseAllRequests() {
requestTracker.pauseAllRequests();
}
/**
* Performs {@link #pauseAllRequests()} recursively for all managers that are contextually
* descendant to this manager based on the Activity/Fragment hierarchy.
* 递归暂停所有RequestManager的请求。
*/
public synchronized void pauseAllRequestsRecursive() {
pauseAllRequests();
for (RequestManager requestManager : treeNode.getDescendants()) {
requestManager.pauseAllRequests();
}
}
......
......
省略大部分操作
以上对于请求的处理,都是加锁的,因此我们需要尽量避免这些方法的调用。而最终处理这些特殊情况的类是RequestTracker。这是一开始就被new出来的。来看看请求跟踪器是怎么处理这些请求的:
//需要执行的请求,弱引用的hashMap作为容器,以防请求过多导致的OOM,再使用set去重
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//特殊情况导致中断的请求,set去重
private final Set<Request> pendingRequests = new HashSet<>();
......
......
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");
}
//请求放入待处理的set中
pendingRequests.add(request);
}
}
public void pauseRequests() {
isPaused = true;
for (Request request : Util.getSnapshot(requests)) {
if (request.isRunning()) {
// Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
// in the UI, while still making sure that any in progress parts of requests are immediately
// stopped.
request.pause();
pendingRequests.add(request);
}
}
}
......
......
RequestTracker将请求分为两种,正常执行的请求和异常状态的请求,分别存放在两个set中,而RequestTracker是与RequestManager一一绑定,Glide使用这种方法将请求分类,统一处理。异常状态下将正常请求清除,释放内存,将请求加入到待处理的set中,以便在状态恢复正常时继续加载。
RequestManager.load(url)=>RequestBuilder
先看代码:
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
.......
.......
源码中重载方法有很多,只是传入对象有Drawable,String,BitMap等等。接下来看看asDrawable做了什么。
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
@NonNull
@CheckResult
public RequestBuilder<File> downloadOnly() {
return as(File.class).apply(DOWNLOAD_ONLY_OPTIONS);
}
都是为了构造出一个RequestBuilder对象,来看看这个RequestBuilder是做什么工作的:
RequestBuilder
/**
* A generic class that can handle setting options and staring loads for generic resource types.
* 一个可以处理加载选项以及开始加载通用资源类型的类。
* 继承自BaseRequestOptions,这是一个封装了所有加载选项,也就是自带图片加载功能,例如缩略图,是否缓存,圆角等等。
* 因此Glide将使用者多样化的加载请求参数都封装在该类中,对图片加载请求做定制化处理。
*/
大部分情况下加载图片都是使用ImageView作为容器显示的,因此大部分RequestBuilder的resourceClass都是Drawable.class,只有一种特殊情况,就是当只下载不显示时,是File.class。
接下来,看看RequestBuilder.load做了什么操作。
RequestBuilder.load(T t)=>RequestBuilder
同样,load方法根据传入参数不同,也有很多重载方法:
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
@NonNull
@CheckResult
@Override
public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
@NonNull
@Override
@CheckResult
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
......
......
所有的重载方法要么返回的是loadGeneric,要么是追加了一个apply方法,loadGeneric返回的是RequestBuilder本身,apply方法也是一样,那么这两个方法对于RequestBuilder分别做了什么处理呢?
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
//默认false,通过调用autoClone方法修改为true,是否需要深拷贝
if (isAutoCloneEnabled()) {
return clone().loadGeneric(model);
}
//可以理解为数据来源
this.model = model;
//标志位,在into方法校验,目的是不能在没有调用load方法的情况下调用into
isModelSet = true;
return selfOrThrowIfLocked();
}
@NonNull
@SuppressWarnings("unchecked")
protected final T selfOrThrowIfLocked() {
//isLocked默认为false,可以调用lock方法更改为true
if (isLocked) {
throw new IllegalStateException("You cannot modify locked T, consider clone()");
}
return self();
}
从代码中可以看出,只是设置了两个标志位,做了是否需要深拷贝以及是否被锁的判断,还算简单。那apply做了什么呢?
public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
Preconditions.checkNotNull(requestOptions);
return super.apply(requestOptions);
}
public T apply(@NonNull BaseRequestOptions<?> o) {
if (isAutoCloneEnabled) {
return clone().apply(o);
}
BaseRequestOptions<?> other = o;
if (isSet(other.fields, SIZE_MULTIPLIER)) {
sizeMultiplier = other.sizeMultiplier;
}
if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {
useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;
}
//都是相似方法,校验当前属性是否和参数一致,不一致就重新赋值
......
......
......
if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {
onlyRetrieveFromCache = other.onlyRetrieveFromCache;
}
// Applying options with dontTransform() is expected to clear our transformations.
if (!isTransformationAllowed) {
transformations.clear();
fields &= ~TRANSFORMATION;
isTransformationRequired = false;
fields &= ~TRANSFORMATION_REQUIRED;
isScaleOnlyOrNoTransform = true;
}
fields |= other.fields;
options.putAll(other.options);
return selfOrThrowIfLocked();
}
private static boolean isSet(int fields, int flag) {
//这里的设计很巧妙,阅读源码可以发现,已经定义的所有flag都是将1左移不同位,以一个int值存储,而fields默认为0,
//设置一个属性就将对应位置为1,因此做一次&运算就可以知道是flag是否一致。
return (fields & flag) != 0;
}
通过代码得知,apply方法实际上就是做了一次巧妙的赋值,将新设置的属性赋值到当前的RequestBuilder中。
总结
本次涉及到的关键类之间的关系如下:
这一次的源码学习,弄清楚了一些之前理解不到的问题:
1.app在后台运行图片加载会暂停吗?再次前台运行怎么保证图片继续加载?
2.Glide是如何处理不同的缩略图,圆角属性,缓存模式?
对于位运算的运用,很是巧妙,对于日后处理相似场景很有帮助。
load方法不load,Glide到底怎么load?