
Glide源碼剖析系列
- Android Glide源碼剖析系列(一)圖片加載請(qǐng)求如何感知組件生命周期
- Android Glide源碼剖析系列(二)Glide如何管理圖片加載請(qǐng)求
- Android Glide源碼剖析系列(三)深入理解Glide圖片加載流程
- Android Glide源碼剖析系列(四)緩存機(jī)制及其原理
為什么選擇Glide?
- 多種圖片格式的緩存,適用于更多的內(nèi)容表現(xiàn)形式(如Gif、WebP、縮略圖、Video)
- 生命周期集成(根據(jù)Activity或者Fragment的生命周期管理圖片加載請(qǐng)求)Glide可以感知調(diào)用頁(yè)面的生命周期,這就是優(yōu)勢(shì)
- 高效處理Bitmap(bitmap的復(fù)用和主動(dòng)回收,減少系統(tǒng)回收壓力)
- 高效的緩存策略,靈活(Picasso只會(huì)緩存原始尺寸的圖片,Glide緩存的是多種規(guī)格),加載速度快且內(nèi)存開(kāi)銷小(默認(rèn)Bitmap格式的不同,使得內(nèi)存開(kāi)銷是Picasso的一半)
小結(jié):支持圖片格式多;Bitmap復(fù)用和主動(dòng)回收;生命周期感應(yīng);優(yōu)秀的緩存策略;加載速度快(Bitmap默認(rèn)格式RGB565)
Glide簡(jiǎn)單使用
Glide.with(this)
.load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
.into(imageView);
源碼分析
通過(guò)Android Glide源碼解析系列(一)圖片加載請(qǐng)求如何感知組件生命周期一文我們知道,with()方法返回的是一個(gè)RequestManager對(duì)象,說(shuō)明load()方法是在RequestManager類當(dāng)中的,所以我們首先要看的就是RequestManager這個(gè)類。

load()方法支持多種形式的圖片來(lái)源,本文以RequestManager類中的 load(String string) 方法為例進(jìn)行深入分析。
@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);
}
為了使邏輯更清晰,把上面三個(gè)方法合并成一行代碼來(lái)分析
RequestManager#load(String string)
//相當(dāng)于
new RequestBuilder<>(glide, RequestManager.this, Drawable.class, context).load(string);
load(String string)最終被分為兩步執(zhí)行:
- 以
Drawable.class為參數(shù)創(chuàng)建RequestBuilder實(shí)例
RequestBuilder構(gòu)造函數(shù)
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass; //重要變量賦值為Drawable.class,構(gòu)建ImageViewTarget的時(shí)候使用
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners()); //初始化默認(rèn)請(qǐng)求監(jiān)聽(tīng)
apply(requestManager.getDefaultRequestOptions()); //應(yīng)用默認(rèn)配置信息
}
- 調(diào)用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, 構(gòu)建ImageViewTarget的時(shí)候使用
- model = 圖片來(lái)源為String地址,構(gòu)建Request的時(shí)候使用
RequestManager#load(String string)分析到現(xiàn)在,似乎都是一些準(zhǔn)備工作,真正的圖片加載還沒(méi)有出現(xiàn)。既然“千呼萬(wàn)喚不出來(lái)”,那咱們就“打破砂鍋查到底”,繼續(xù)查看RequestBuilder#into(ImageView view)方法
#RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread(); //判斷是否在主線程執(zhí)行
Preconditions.checkNotNull(view);
//把ImageView的scaleType寫進(jìn)requestOptions
//省略代碼
return into(
//注釋1:構(gòu)建新的ImageViewTarget
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions, //請(qǐng)求選項(xiàng)
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:構(gòu)建新的加載請(qǐng)求Request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest(); //獲取目標(biāo)View上已經(jīng)存在的舊請(qǐng)求
if (request.isEquivalentTo(previous) //兩次的請(qǐng)求相同 && !(跳過(guò)內(nèi)存緩存 && 舊請(qǐng)求已完成)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) { //如果舊請(qǐng)求未開(kāi)始
previous.begin(); //啟動(dòng)舊請(qǐng)求
}
return target;
}
//取消Glide為target準(zhǔn)備的所有加載請(qǐng)求,并釋放已經(jīng)
//加載的資源(例如Bitmap),以便它們可以被重用。
requestManager.clear(target);
//利用View.setTag把request請(qǐng)求綁定到target指向的View
target.setRequest(request);
//注釋3
requestManager.track(target, request);
return target;
}
private boolean isSkipMemoryCacheWithCompletePreviousRequest(
BaseRequestOptions<?> options, Request previous) {
return !options.isMemoryCacheable() && previous.isComplete();
}
小結(jié):
- 調(diào)用方法
glideContext.buildImageViewTarget(view, transcodeClass)創(chuàng)建新的ImageViewTarget - 創(chuàng)建新的加載請(qǐng)求request,并且檢查target上是否已有老請(qǐng)求previous:
2.1 如果兩次的請(qǐng)求相同 && !(跳過(guò)內(nèi)存緩存 && 舊請(qǐng)求已完成),into()方法直接返回target
2.2 如果滿足2.1且 && 請(qǐng)求未開(kāi)始,先啟動(dòng)舊請(qǐng)求再返回target
2.3 如果2.1和2.2都不滿足,依次執(zhí)行:取消Glide為target準(zhǔn)備的所有加載請(qǐng)求,并釋放已經(jīng)加載的資源(例如Bitmap),以便它們可以被重用;利用View.setTag把新請(qǐng)求綁定到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)");
}
}
}
根據(jù)transcodeClass類型創(chuàng)建對(duì)應(yīng)的ImageViewTarget,即Drawable.class -> DrawableImageViewTarget
代碼注釋2分析:Request創(chuàng)建過(guò)程比較復(fù)雜,還要處理縮略圖加載請(qǐng)求、加載錯(cuò)誤圖片加載請(qǐng)求等等,因此我們只分析主線buildRequest -> buildRequestRecursive -> buildThumbnailRequestRecursive -> obtainRequest -> SingleRequest.obtain -> new SingleRequest()。最終創(chuàng)建出一個(gè)SingleRequest實(shí)例
代碼注釋3分析:
#RequestManager
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
主要做了兩件事:
- target添加到targetTracker,管理target指向的View生命周期回調(diào)事件
- request添加到requestTracker,管理圖片加載請(qǐng)求
targetTracker和requestTracker都是RequestManager的成員變量,分別負(fù)責(zé)管理target和request
TargetTracker源碼:
/**
* TargetTracker 職責(zé)是維護(hù)一個(gè)Target列表,統(tǒng)一管理所有Target的onStart()、
* onStop()和onDestroy()方法回調(diào)事件
*/
public final class TargetTracker implements LifecycleListener {
//維護(hù)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強(qiáng)引用,可能會(huì)發(fā)生內(nèi)存泄漏,因此將request作為key存儲(chǔ)在WeakHashMap里
//面,如果這些key不再使用,WeakHashMap會(huì)自動(dòng)異常這些key,從而避免內(nèi)存泄漏。
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//直接持有未完成和等待開(kāi)始的request強(qiáng)引用對(duì)象,防止在運(yùn)行過(guò)程中被GC
private final Set<Request> pendingRequests = new HashSet<>();
//所有請(qǐng)求是否已經(jīng)被暫停
private boolean isPaused;
/** Starts tracking the given request. 把請(qǐng)求添加到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. 停止正在運(yùn)行的請(qǐng)求*/
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. 開(kāi)啟未完成或失敗的請(qǐng)求*/
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();
}
//省略其他方法
}
圖片加載請(qǐng)求的管理流程END
結(jié)語(yǔ)
Glide管理圖片加載請(qǐng)求的方式并不復(fù)雜:使用一個(gè)請(qǐng)求管理類RequestTracker 維護(hù)所有請(qǐng)求列表,并且提供統(tǒng)一操作所有請(qǐng)求的方法(即所有請(qǐng)求的開(kāi)啟、暫停和重啟等等方法),RequestManager只要通過(guò)一個(gè)RequestTracker 對(duì)象就能輕輕松松控制圖片加載請(qǐng)求。
下一篇文章我們將會(huì)學(xué)習(xí)Glide如何啟動(dòng)圖片加載請(qǐng)求,以及圖片資源是如何經(jīng)歷重重改造最終顯示到界面上,敬請(qǐng)期待!