轉載請标明位址 QuincySx: http://www.jianshu.com/p/eed7054e3722
這是 Glide 的第二篇,在上一篇中講的都是大概流程,直接閱讀起來可能比較困難,推薦結合源碼浏覽,在這一篇中就講資源加載,是以貼上來的源碼就會多一些。
public Target<TranscodeType> into(ImageView view) {
.....
//調用 (glide.buildImageViewTarget()
return into(glide.buildImageViewTarget(view, transcodeClass));
}
然後在調用以下方法 這個地方無論調什麼都是生成 ViewTarget 就不細追究了
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
建立完 ViewTarget 之後調用 init()
public <Y extends Target<TranscodeType>> Y into(Y target) {
...
//在 Target 中擷取請求對象
Request previous = target.getRequest();
//如果有請求對象則把它清掉
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//重新建構新的請求對象 并設定到 target 中,添加生命周期監聽
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
//開始請求資源
requestTracker.runRequest(request);
return target;
}
然後我們進去到 RequestTracker 的 runRequestra() 方法中
public void runRequest(Request request) {
//先把請求添加到隊列中,然後判斷這個隊列是不是暫停狀态,暫停的話就放到暫停清單裡,不是暫停的話就開始運作
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
簡單說一下 RequestTracker 請求隊列,為什麼這裡要維護一個暫停或運作的狀态呢,因為 RequestTracker 的生命周期是跟随 RequestManager 的,如果你看過 Glide 往頁面中添加 Fragment 的那個步驟的話,你就會發現 RequestManager 是在 Fragment 中維護的,他同樣監聽這 Fragment 的顯示狀态,通俗點說就是一個顯示的頁面那麼請求隊列的狀态就是運作,其他已不顯示的頁面 隊列就會成為暫停狀态,因為隊列是監聽 Fragment 的生命周期的,會動态調整每個頁面請求隊列的狀态,已達到節省系統資源的目的。
接下來再看 request.begin() 方法
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
//更新請求的狀态
status = Status.WAITING_FOR_SIZE;
//因為如果沒有設定縮小 overrideWidth,overrideHeight 預設為 -1
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果設定了圖檔縮小,并且重新設定的寬高大于0 直接調用 onSizeReady
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果沒有設定了圖檔縮小則去計算 View 本身的寬高
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
//設定占位圖檔
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
再看 onSizeReady 方法之前我們先看一下 ViewTager 的 getSize 方法
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
//擷取View 的寬高
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
//寬高不為0回調 onSizeReady
cb.onSizeReady(currentWidth, currentHeight);
} else {
//寬高為0 就監聽 View 的繪制之前 的事件再去獲得寬高,回調進行加載
// We want to notify callbacks in the order they were added and we only expect one or two callbacks to
// be added a time, so a List is a reasonable choice.
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}
//檢視 onSizeReady 方法
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));
}
}
//我們接下來看一下 engine 的 load 方法 在調用 load 方法的時候,看到最後有一個 this 參數這是一個回調接口 這個地方注意一下
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());
//根據 key 在記憶體緩存中擷取緩存
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
//如果不為 null 則調用完成的回調接口
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;
}
//jobs 是一個正在運作的任務集合,擷取 key 相同的任務 防止有相同的請求正在運作
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 = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
//在建立 EngineRunnable 的時候他把 engineJob 也傳了進去而他繼承自
//EngineRunnable.EngineRunnableManager 這個地方記住
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
//目前請求加入 jobs 維護
jobs.put(key, engineJob);
//添加回調到 GenericRequest 的方法
engineJob.addCallback(cb);
//運作任務
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
接下來我們再看 engineJob.start(runnable); 方法
public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
//因為 EngineRunnable 是繼承的 Runnable 是以執行 EngineRunnable 的 run 方法
//還要注意的是這個地方是 在 緩存服務中送出 了工作線程
future = diskCacheService.submit(engineRunnable);
}
我們接着看 EngineRunnable 的 run 方法 ,因為這個方法會走兩次稍微注意一下
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
//開始獲得資料
resource = decode();
} catch (OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Out Of Memory Error decoding", e);
}
exception = new ErrorWrappingGlideException(e);
} 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;
}
//檢視資源加載是否成功
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
//這個方法将會調用兩次
//第一次:因為在構造中 this.stage = Stage.CACHE 是以第一次肯定調用 decodeFromCache() 方法
//第二次:因為 stage = Stage.SOURCE 狀态改變 是以調用 decodeFromSource()
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
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();
}
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
//如果資源加載失敗會把stage 加載狀态修改 然後調用 回調接口去請求資源
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
//如果資源請求還失敗就會抛出異常
manager.onException(e);
}
}
如果加載資源的話 會調用 decodeJob.decodeFromSource()
public Resource<Z> decodeFromSource() throws Exception {
//擷取資源
Resource<T> decoded = decodeSource();
//資源轉換以後再說不在這篇文章的讨論範圍内
return transformEncodeAndTranscode(decoded);
}
我們先看擷取資源的方法
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
//它是通過加載器 fetcher 來擷取資源 我們的環境是(加載 String 的 Url 并且沒有配置第三方網絡加載器)
//那麼 fetcher 在哪裡來的呢 還記不記得 GenericRequest 的 onSizeReady() 方法 ,他是從 modelLoader.getResourceFetcher() 擷取的 那麼 modelLoader 哪來的呢
//你可以去 Glide 構造裡發現 register(String.class, InputStream.class, new StreamStringLoader.Factory()); 這麼一句代碼
//我們看到了 StreamStringLoader 這個類 但是并沒有發現 getResourceFetcher() 方法,我們看一下他的父類 StringLoader 現在發現了 getResourceFetcher 方法 在看到父類的時候我們又發現 父類是一個帶參的構造,StreamStringLoader 在構造的時候查找了 Uri 的加載器給了父類 (查找就在流程我就不細分析了,自己看源碼吧)
//我們又在 Glide 構造裡查到 Uri 的加載器是 StreamUriLoader
//進去有一看 StreamUriLoader 繼承自構造 UriLoader 的時候 傳入了 GlideUrl 類型的加載器
//接着找到 HttpUrlGlideUrlLoader ,我們接着看 UriLoader 的 getResourceFetcher() 方法 他判斷了資源是本地資源還是網絡資源,本地資源就直接加載,方法自己看一下吧,否則就調用 HttpUrlGlideUrlLoader 進行網絡加載
// HttpUrlGlideUrlLoader 的 getResourceFetcher 是個 HttpUrlFetcher 調用 loadData 進行網絡加載,怎麼加載的代碼自己看一下吧,我就不貼了
//這個地方比較亂,大家慢慢理一下
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
//如果打開磁盤緩存就将在的資源緩存起來,然後再拿出來 ,并且裝換成 Resource
//如果沒有開啟磁盤緩存 就直接轉換為 Resource
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
圖檔的變換下篇文章講,如果沒有什麼妖蛾子的話該調用 onLoadComplete() 方法,回調 EngineJob.onResourceReady(resource); 的方法 ,接着往下跟蹤發現來到了如下方法
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;
//記錄是否回調的标志 此時 acquire 為 1
engineResource.acquire();
//回調 Engine 的 onEngineJobComplete 的方法做了這麼幾件事件事
//1. 添加 Engine 的回調
//2. 緩存資源
//3. 将任務在 jobs 中删除任務
listener.onEngineJobComplete(key, engineResource);
//調用所有等待此資源加載的回調
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
//回調一次 acquire 的數值加 1
engineResource.acquire();
//回調 GenericRequest 的 onResourceReady 代碼看下方
cb.onResourceReady(engineResource);
}
}
//檢視acquire 的值減 1 是否等于0,如果等于零,就說明此資源沒有任何回調,則
回調Engine 的 onResourceReleased 在活動資源緩存中删除,并且判斷是否緩存到記憶體中,然後清理釋放資源
engineResource.release();
}
接下來看 GenericRequest 的 onResourceReady() 方法
資源有了剩下的就是将它放到 imageView 上
public void onResourceReady(Resource<?> resource) {
...
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
//如果資源為 NULL 則清除掉活動資源,并緩存
releaseResource(resource);
...
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
繼續檢視 onResourceReady 方法
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
//往上查 requestListener 這個參數,發現是在 GenericRequestBuilder 中的 buildRequestRecursive 方法中,咱們的情景設定是沒有設定縮放,是以 requestListener 是 null
的
//requestListener 可以在外面設定Glide 加載失敗或成功的監聽
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 的 onException 方法
status = Status.FAILED; //修改狀态
//TODO: what if this is a thumbnail request?
//requestListener 可以在外面設定Glide 加載失敗或成功的監聽
if (requestListener == null || !requestListener.onException(e, model, target, isFirstReadyResource())) {
setErrorPlaceholder(e); //給view設定錯誤占位符
}
到此資源的擷取加載流程就完了
小結
到此我們已經簡單的分析了一遍圖檔資源在網絡上加載,并且設定到 view 中,歡迎大家品嘗,并提出意見
注:
本篇基于 Glide 3.8.0