不得不說,Glide
真的是十分難啃,來來回回看了不少的文章,並對照着源碼分析了一遍,才理清了大概的思路,但願這篇文章能對你們有必定的幫助。java
在進入正題以前讓咱們先談一些題外話,就是 爲何咱們要去看 Glide 的源碼。android
若是你們有閱讀過以前的五篇關於Glide
使用的教程:程序員
Glide 知識梳理(1) - 基本用法
Glide 知識梳理(2) - 自定義 Target
Glide 知識梳理(3) - 自定義 transform
Glide 知識梳理(4) - 自定義 animate
Glide 知識梳理(5) - 自定義 GlideModuleapi
能夠發現其實Glide
的功能已經很完備了,不管是佔位符、錯誤圖片仍是請求完後對於返回圖片的變換,都提供瞭解決的方案,徹底能夠知足平常的需求。數組
那麼,咱們爲何要花費大量的時間去看Glide
的源碼呢,我本身的觀點是如下幾點:緩存
API
的原理。在以前介紹使用的幾篇文章中,咱們談到了許多的方法,例如placeholder/error/...
,還講到了自定義transform/target/animate
。可是因爲Glide
將其封裝的很好,僅僅經過簡單使用你根本沒法瞭解這些用法最後是如何生效的,只有經過閱讀源碼才能明白。ImageLoader
,仍是後來的Picasso
、fresco
,對於一個圖片加載框架來講,都離不開三點:請求管理、工做線程管理和圖片緩存管理。閱讀源碼將有助於咱們學習到圖片請求框架對於這三個核心問題的解決方案,這也是我認爲 最關鍵的一點。Glide
的架構設計,對於Glide
來講,這一點可能適合於高水平的程序員,由於實在是太複雜了。在閱讀源碼以前,仍是要作一些準備性的工做的,否則你會發現沒過多久你就想放棄了,個人準備工做分爲如下幾步:網絡
Glide
的高級用法。千萬不要知足於調用load
方法加載出圖片就知足了,要學會去了解它的一些高級用法,例如在 Glide 知識梳理(5) - 自定義GlideModule 一文中介紹如何自定義ModelLoader
,你就會對ModelLoader
有個大概的印象,知道它是用來作什麼的,否則在源碼中看到這個類的時候確定會一臉懵逼,沒多久就放棄了。Demo
入手,經過 斷點的方式,一步步地走,觀察每一個變量的類型和值。咱們從最簡單的例子入手,加載一個網絡的圖片地址,並在ImageView
上展現。架構
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(mImageView);
複製代碼
這上面的鏈式調用分爲三步,其中前兩步是進行一些準備工做,真正進行處理的邏輯是在第三步當中: app
with(Activity activity)
是Glide
的一個靜態方法,當調用該方法以後會在Activity
中添加一個Glide
內部自定義的RequestManagerFragment
,當Activity
的狀態發生變化時,該Fragment
的狀態也會發生相應的變化。通過一系列的調用,這些變化將會通知到RequestManager
。RequestManager
則經過RequestTracker
來管理全部的Request
,這樣RequestManager
在處理請求的時候,就能夠根據Activity
當前的狀態進行處理。 框架
with
方法會返回一個RequestManager
對象,接下來第二步。
在 Glide 知識梳理(1) - 基本用法 中,咱們學習了許多load
的重載方法,能夠從url
、SDCard
和byte[]
數組中來加載。這一步的目的是根據load
傳入的類型,建立對應的DrawableTypeRequest
對象,DrawableTypeRequest
的繼承關係以下所示,它是Request
請求的建立者,真正建立的邏輯是在第三步中建立的。
load
方法最終都會經過
loadGeneric(Class<T> class)
方法來建立一個
DrawableTypeRequest
對象,在
DrawableTypeRequest
建立時須要傳入兩個關鍵的變量:
streamModelLoader
fileDescriptorModelLoader
這兩個變量的類型均爲ModelLoader
,看到這個是否是有似曾相識的感受,沒錯,在 Glide 知識梳理(5) - 自定義GlideModule 中咱們介紹了若是經過OkHttpClient
來替換HttpURLConnection
時就已經介紹了它,這 兩個變量決定了獲取圖片資源的方式。
如今,只要明白上面這點就能夠了,後面在用到它其中的成員變量時,咱們再進行詳細的分析。
下面,咱們來看真正的重頭戲,即into
方法執行過程,該方法中包含了加載資源,設置資源到對應目標的一整套邏輯。
public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> implements Cloneable {
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
//若是是 ImageView,那麼返回的是 GlideDrawableImageViewTarget。
return into(glide.buildImageViewTarget(view, transcodeClass));
}
}
複製代碼
因爲DrawableTypeRequest
並無重寫into
方法,所以會調用到它的父類DrawableRequestBuilder
的into(ImageView)
方法中,DrawableRequestBuilder
又會調用它的父類GenericRequestBuilder
的into(ImageView)
方法。
這裏首先會根據view
的scaleType
進行變換,而後再經過Glide.buildImageViewTarget
方法建立Target
,Target
的含義是 資源加載完畢後所要傳遞的對象,也就是整個加載過程的終點,對於ImageView
來講,該方法建立的是GlideDrawableImageViewTarget
。
public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> implements Cloneable {
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
//若是該 Target 以前已經有關聯的請求,那麼要先將以前的請求取消。
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//建立 Request,並將 Request 和 Target 關聯起來。
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
//這一步會觸發任務的執行。
requestTracker.runRequest(request);
return target;
}
}
複製代碼
接下來,看GenericRequestBuilder
中的into(Target)
方法,在into
方法中會經過buildRequest
方法建立Request
,並將它和Target
進行 雙向關聯,Request
的實現類爲GenericRequest
。
在建立完Request
以後,經過RequestTracker
的runRequest
嘗試去執行任務。
public class RequestTracker {
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
}
複製代碼
RequestTracker
會判斷當前界面是否處於可見狀態,若是是可見的,那麼就調用Request
的begin
方法發起請求,不然就先將請求放入到等待隊列當中,Request
的實現類爲GenericRequest
。
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback, ResourceCallback {
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
//首先判斷寬高是否有效,若是有效那麼就調用 onSizeReady。
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//先獲取寬高,最終也會調用到 onSizeReady 方法。
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
//通知目標回調。
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
//調用 Engine 的 load 方法進行加載。
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
}
複製代碼
在GenericRequest
的begin()
方法中,首先判斷寬高是否有效,若是有效那麼就調用onSizeReady
方法,假如寬高無效,那麼會先計算寬高,計算完以後也會調用onSizeReady
,這裏面會調用Engine
的load
方法去加載。
public class Engine implements EngineJobListener, MemoryCache.ResourceRemovedListener, EngineResource.ResourceListener {
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher, DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder, Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
//建立緩存的`key`。
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//一級內存緩存,表示緩存在內存當中,而且目前沒有被使用的資源。
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//二級內存緩存,表示緩存在內存當中,而且目前正在被使用的資源。
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//若是當前任務已經在執行,那麼添加回調後返回。
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//EngineJob 對應於一個任務。
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
//DecodeJob 對應於任務的處理者。
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
//包含了任務以及任務的處理。
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//將該 Runnable 放入到線程池當中,執行時會調用 run() 方法。
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
}
複製代碼
在Engine
的begin
方法中,會在內存中查找是否有緩存,若是在內存當中找不到緩存,那麼就會建立EngineJob
、DecodeJob
和EngineRunnable
這三個類,嘗試從數據源中加載資源,觸發的語句爲engineJob.start(runnable)
。
class EngineRunnable implements Runnable, Prioritized {
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
//decode 方法返回 Resource 對象。
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
//若是成功加載資源,那麼就會回調onLoadComplete方法。
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
//從本地緩存中獲取。
return decodeFromCache();
} else {
//從源地址中獲取。
return decodeFromSource();
}
}
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
}
複製代碼
EngineJob
的start
方法會經過內部線程池的submit
方法提交任務,當任務被調度執行時,會調用到EngineRunnable
的run()
方法。
在run()
方法中,會根據任務的類型來判斷是從磁盤緩存仍是從原始數據源中獲取資源,即分別調用DecodeJob
的decodeFromCache
或者decodeFromSource
,最終會將資源封裝爲Resource<T>
對象。
假如成功獲取到了資源,那麼會經過manager.onResourceReady
返回,manager
的類型爲EngineRunnableManager
,其實現類爲以前咱們看到的EngineJob
。
class EngineJob implements EngineRunnable.EngineRunnableManager {
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
// synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
//ResourceCallback 的實現類爲 GenericRequest
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
}
複製代碼
在EngineJob
的onResourceReady
方法中,會將Resource
經過Handler
的方法傳遞到主線程,並調用handleResultOnMainThread
,注意該方法中帶有註釋的部分,這裏的ResourceCallback
就是咱們以前看到GenericRequest
。
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback, ResourceCallback {
@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("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.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
//通知目標資源已經獲取到了。
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
}
複製代碼
在GenericRequest
的onResourceReady
方法中,會調用Target
的onResourceReady
方法,也就是咱們最開始講到的GlideDrawableImageViewTarget
。
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
if (!resource.isAnimated()) {
//TODO: Try to generalize this to other sizes/shapes.
// This is a dirty hack that tries to make loading square thumbnails and then square full images less costly
// by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions.
// If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions,
// the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers
// lots of these calls and causes significant amounts of jank.
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
/** * Sets the drawable on the view using * {@link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}. * * @param resource The {@link android.graphics.drawable.Drawable} to display in the view. */
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
}
複製代碼
在GlideDrawableImageViewTarget
的onResourceReady
方法中,會首先回調父類的super.onResourceReady
,父類的該方法又會回調setResource
方法,而GlideDrawableImageViewTarget
的setResource
就會經過setResource
將加載好的圖片資源設置進去。
因爲夾雜着代碼和文字看着比較亂,整個的流程圖以下所示,並在有道雲筆記上整理了一下關鍵的類和函數調用語句,直達連接。
這篇文章目的是讓你們對整個流程有一個大體的瞭解,因此對於不少細節問題沒有深究,例如:
Engine
的load()
方法中,會執行兩次內存緩存的判斷,這裏面實現的機制是怎麼樣的?EngineRunnable
的decode()
方法中,採用DecodeJob
去數據源加載資源,資源加載以及解碼的過程是怎麼樣的?EngineRunnable
中回調給Engine
的Resource<T>
是一個什麼樣的對象?對於以上的這些問題,會在後面單獨的一個個章節進行分析。