Android Glide源码剖析系列(二)Glide如何管理图
![](https://img.haomeiwen.com/i6337201/da422e06e95205df.png)
Glide源码剖析系列
- Android Glide源码剖析系列(一)图片加载请求如何感知组件生命周期
- Android Glide源码剖析系列(二)Glide如何管理图片加载请求
- Android Glide源码剖析系列(三)深入理解Glide图片加载流程
- Android Glide源码剖析系列(四)缓存机制及其原理
为什么选择Glide?
- 多种图片格式的缓存,适用于更多的内容表现形式(如Gif、WebP、缩略图、Video)
- 生命周期集成(根据Activity或者Fragment的生命周期管理图片加载请求)Glide可以感知调用页面的生命周期,这就是优势
- 高效处理Bitmap(bitmap的复用和主动回收,减少系统回收压力)
- 高效的缓存策略,灵活(Picasso只会缓存原始尺寸的图片,Glide缓存的是多种规格),加载速度快且内存开销小(默认Bitmap格式的不同,使得内存开销是Picasso的一半)
小结:支持图片格式多;Bitmap复用和主动回收;生命周期感应;优秀的缓存策略;加载速度快(Bitmap默认格式RGB565)
Glide简单使用
Glide.with(this)
.load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
.into(imageView);
源码分析
通过Android Glide源码解析系列(一)图片加载请求如何感知组件生命周期一文我们知道,with()方法返回的是一个RequestManager对象,说明load()方法是在RequestManager类当中的,所以我们首先要看的就是RequestManager这个类。
![](https://img.haomeiwen.com/i6337201/00c66a5337dca318.png)
load()方法支持多种形式的图片来源,本文以RequestManager类中的 load(String string)
方法为例进行深入分析。
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
@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);
}
为了使逻辑更清晰,把上面三个方法合并成一行代码来分析
RequestManager#load(String string)
//相当于
new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);
load(String string)最终被分为两步执行:
- 以
Drawable.class
为参数创建RequestBuilder实例
RequestBuilder构造函数
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass; //重要变量赋值为Drawable.class,构建ImageViewTarget的时候使用
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners()); //初始化默认请求监听
apply(requestManager.getDefaultRequestOptions()); //应用默认配置信息
}
- 调用RequestBuilder#load(String string)
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
if (isAutoCloneEnabled()) {
return clone().loadGeneric(model);
}
this.model = model; //String变量赋值给model
isModelSet = true;
return selfOrThrowIfLocked();
}
RequestBuilder重要成员变量:
- transcodeClas = Drawable.class, 构建ImageViewTarget的时候使用
- model = 图片来源为String地址,构建Request的时候使用
RequestManager#load(String string)
分析到现在,似乎都是一些准备工作,真正的图片加载还没有出现。既然“千呼万唤不出来”,那咱们就“打破砂锅查到底”,继续查看RequestBuilder#into(ImageView view)
方法
#RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread(); //判断是否在主线程执行
Preconditions.checkNotNull(view);
//把ImageView的scaleType写进requestOptions
//省略代码
return into(
//注释1:构建新的ImageViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions, //请求选项
Executors.mainThreadExecutor()); //主线程池,切回到主线程显示图片
}
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
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(); //获取目标View上已经存在的旧请求
if (request.isEquivalentTo(previous) //两次的请求相同 && !(跳过内存缓存 && 旧请求已完成)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) { //如果旧请求未开始
previous.begin(); //启动旧请求
}
return target;
}
//取消Glide为target准备的所有加载请求,并释放已经
//加载的资源(例如Bitmap),以便它们可以被重用。
requestManager.clear(target);
//利用View.setTag把request请求绑定到target指向的View
target.setRequest(request);
//注释3
requestManager.track(target, request);
return target;
}
private boolean isSkipMemoryCacheWithCompletePreviousRequest(
BaseRequestOptions<?> options, Request previous) {
return !options.isMemoryCacheable() && previous.isComplete();
}
小结:
- 调用方法
glideContext.buildImageViewTarget(view, transcodeClass)
创建新的ImageViewTarget - 创建新的加载请求request,并且检查target上是否已有老请求previous:
2.1 如果两次的请求相同 && !(跳过内存缓存 && 旧请求已完成)
,into()方法直接返回target
2.2 如果满足2.1且 && 请求未开始
,先启动旧请求再返回target
2.3 如果2.1和2.2都不满足,依次执行:取消Glide为target准备的所有加载请求,并释放已经加载的资源(例如Bitmap),以便它们可以被重用;利用View.setTag把新请求绑定到target指向的View;requestManager重新管理target和request。
代码注释1分析:
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
//transcodeClas = Drawable.class
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
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)) { //transcodeClas = Drawable.class
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
根据transcodeClass类型创建对应的ImageViewTarget,即Drawable.class -> DrawableImageViewTarget
代码注释2分析:Request创建过程比较复杂,还要处理缩略图加载请求、加载错误图片加载请求等等,因此我们只分析主线buildRequest -> buildRequestRecursive -> buildThumbnailRequestRecursive -> obtainRequest -> SingleRequest.obtain -> new SingleRequest()。最终创建出一个SingleRequest实例
代码注释3分析:
#RequestManager
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
主要做了两件事:
- target添加到targetTracker,管理target指向的View生命周期回调事件
- request添加到requestTracker,管理图片加载请求
targetTracker和requestTracker都是RequestManager的成员变量,分别负责管理target和request
TargetTracker源码:
/**
* TargetTracker 职责是维护一个Target列表,统一管理所有Target的onStart()、
* onStop()和onDestroy()方法回调事件
*/
public final class TargetTracker implements LifecycleListener {
//维护target列表
private final Set<Target<?>> targets =
Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
public void untrack(@NonNull Target<?> target) {
targets.remove(target);
}
@Override
public void onStart() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStart();
}
}
@Override
public void onStop() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStop();
}
}
@Override
public void onDestroy() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onDestroy();
}
}
@NonNull
public List<Target<?>> getAll() {
return Util.getSnapshot(targets);
}
public void clear() {
targets.clear();
}
}
RequestTracker部分源码:
public class RequestTracker {
private static final String TAG = "RequestTracker";
// 如果Set直接持有request强引用,可能会发生内存泄漏,因此将request作为key存储在WeakHashMap里
//面,如果这些key不再使用,WeakHashMap会自动异常这些key,从而避免内存泄漏。
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//直接持有未完成和等待开始的request强引用对象,防止在运行过程中被GC
private final Set<Request> pendingRequests = new HashSet<>();
//所有请求是否已经被暂停
private boolean isPaused;
/** Starts tracking the given request. 把请求添加到requests和pendingRequests*/
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);
}
}
/** Stops any in progress requests. 停止正在运行的请求*/
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);
}
}
}
/** Starts any not yet completed or failed requests. 开启未完成或失败的请求*/
public void resumeRequests() {
isPaused = false;
for (Request request : Util.getSnapshot(requests)) {
// We don't need to check for cleared here. Any explicit clear by a user will remove the
// Request from the tracker, so the only way we'd find a cleared request here is if we cleared
// it. As a result it should be safe for us to resume cleared requests.
if (!request.isComplete() && !request.isRunning()) {
request.begin();
}
}
pendingRequests.clear();
}
//省略其他方法
}
图片加载请求的管理流程END
结语
Glide管理图片加载请求的方式并不复杂:使用一个请求管理类RequestTracker 维护所有请求列表,并且提供统一操作所有请求的方法(即所有请求的开启、暂停和重启等等方法),RequestManager只要通过一个RequestTracker 对象就能轻轻松松控制图片加载请求。
下一篇文章我们将会学习Glide如何启动图片加载请求,以及图片资源是如何经历重重改造最终显示到界面上,敬请期待!