圖片回調(diào)的流程
前面的文章詳細介紹了Glide加載圖片的流程尚蝌,這里我們重點回顧一下圖片的的顯示流程:DecodeJob完成圖片的裝載之后楔绞,會回調(diào)到notifyEncodeAndRelease()方法畦浓,之后的流程如下:
上面的流程可以看到遮婶,圖片展示的地方是在Target的onResourceReady()中執(zhí)行的书蚪。這里的Target是哪個步驟構(gòu)建的呢谢肾?回顧一下前面的文章流程中可以知道,在into()方法中钦铺,會構(gòu)建Target订雾,在Request的構(gòu)建中,Target作為參數(shù)傳進Request中矛洞。
自定義Target的原理和實現(xiàn)
上面的圖片回調(diào)流程基本上就介紹了Target的原理洼哎,自定義Target的時候,只需要實現(xiàn)onResourceReady()方法中展示圖片即可缚甩,使用定義Target的用法如下:
SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
imageView.setImageDrawable(resource);
}
};
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(simpleTarget);
以上是基本的自定義Target的用法谱净,掌握了圖片的顯示流程就可以定義復雜的Target窑邦,實現(xiàn)復雜的圖片加載需求擅威。
Preload的功能和原理
preload()可以替換into()法的另外一個方法,和into()不同的是冈钦,preload()方法只加載圖片郊丛,而不顯示圖片,是一種圖片預加載的功能,使真正顯示圖片的時候不需要從網(wǎng)絡獲取厉熟,提高圖片的顯示速度导盅。preload()方法是如何實現(xiàn)不加載圖片的呢?通過前面的圖片加載流程揍瑟,我們知道白翻,獲取圖片之后DecodeJob會執(zhí)行圖片的顯示流程,而圖片顯示是Target完成的绢片。之前的文章分析Glide加載圖片的流程的時候我們知道into()方法滤馍,Glide內(nèi)部邏輯中會構(gòu)建一個Target,這個Target就是顯示圖片的對象底循。我們需要分析preload()內(nèi)部的Target是如何不顯示圖片的巢株。
public Target<TranscodeType> preload() {
return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
}
public Target<TranscodeType> preload(int width, int height) {
final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(requestManager, width, height);
return into(target);
}
我們可以看到:preload()方法會先構(gòu)建一個PreloadTarget對象,然后調(diào)用into(targe)熙涤。所以PreloadTarget類完成了圖片不顯示的邏輯阁苞。我們分析一下PreloadTarget的實現(xiàn):
private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() {
@Override
public boolean handleMessage(Message message) {
if (message.what == MESSAGE_CLEAR) {
((PreloadTarget<?>) message.obj).clear();
return true;
}
return false;
}
});
@Override
public void onResourceReady(Z resource, Transition<? super Z> transition) {
HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
}
可以看到PreloadTarget內(nèi)部在onResourceReady()中僅僅是發(fā)送了一個message,并沒有顯示圖片祠挫。需要注意的是preload()默認是全尺寸緩存圖片的那槽,使用into()顯示preoload()已經(jīng)預加載的圖片時,需要指定緩存策略為DiskCacheStrategy.SOURCE等舔,否則into()會找不到緩存倦炒,從網(wǎng)絡加載圖片。
downloadOnly的功能和原理
into()和preload()方法都是加載圖片的操作软瞎,不提供圖片的路徑信息逢唤,開發(fā)者只關(guān)心圖片顯示的問題,對圖片本身不關(guān)注涤浇,如果我們需要對圖片本身的信息做處理的話鳖藕,就需要知道圖片的保存路徑,Glide提供了兩個方法可以獲取圖片的路徑:
- downloadOnly(int width, int height)
- downloadOnly(Y target)
downloadOnly(int width, int height)方法只锭,該方法主要完成圖片加載不顯示著恩,與preload功能相似,同時他提供了一個獲取圖片緩存路徑的方法蜻展,該方法是阻塞方法喉誊,如果圖片沒有下載成功,會阻塞纵顾,因此一般使用get()方法需要在子線程中調(diào)用伍茄,同時get()內(nèi)部也會檢查是否在子線程,否則拋異常施逾。
downloadOnly(Y target)不同的是不需要再子線程中運行敷矫。
protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
new RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA).priority(Priority.LOW) .skipMemoryCache(true);
public FutureTarget<File> downloadOnly(int width, int height) {
return getDownloadOnlyRequest().submit(width, height);
}
protected RequestBuilder<File> getDownloadOnlyRequest() {
return new RequestBuilder<>(File.class,this).apply(DOWNLOAD_ONLY_OPTIONS);
}
public FutureTarget<TranscodeType> submit(int width, int height) {
final RequestFutureTarget<TranscodeType> target =
new RequestFutureTarget<>(glideContext.getMainHandler(), width, height);
if (Util.isOnBackgroundThread()) {
glideContext.getMainHandler().post(new Runnable() {
@Override
public void run() {
if (!target.isCancelled()) {
into(target, target);
}
}
});
} else {
into(target, target);
}
return target;
}
可以看到downloadOnly(int width, int height)內(nèi)部構(gòu)建的Target是RequestFutureTarget例获,圖片的不顯示功能和get()圖片緩存文件都是在這里提供的。
public synchronized void onResourceReady(R resource, Transition<? super R> transition) {
// Ignored, synchronized for backwards compatibility.
}
public R get() throws InterruptedException, ExecutionException {
try {
return doGet(null);
} catch (TimeoutException e) {
throw new AssertionError(e);
}
}
private synchronized R doGet(Long timeoutMillis)
throws ExecutionException, InterruptedException, TimeoutException {
if (assertBackgroundThread && !isDone()) {
Util.assertBackgroundThread();
}
if (isCancelled) {
throw new CancellationException();
} else if (loadFailed) {
throw new ExecutionException(exception);
} else if (resultReceived) {
return resource;
}
if (timeoutMillis == null) {
waiter.waitForTimeout(this, 0);
} else if (timeoutMillis > 0) {
waiter.waitForTimeout(this, timeoutMillis);
}
if (Thread.interrupted()) {
throw new InterruptedException();
} else if (loadFailed) {
throw new GlideExecutionException(exception);
} else if (isCancelled) {
throw new CancellationException();
} else if (!resultReceived) {
throw new TimeoutException();
}
return resource;
}
RequestFutureTarget的onResourceReady內(nèi)部沒有做任何實現(xiàn)曹仗。而get()方法是個阻塞方法榨汤,如果圖片還沒有加載完成,get()的調(diào)用線程會被阻塞怎茫;同時get()內(nèi)部也做了線程判斷收壕,如果不是在子線程,會拋異常轨蛤。
Listener的功能和原理
listener()方法提供了一個功能:圖片加載的狀態(tài)啼器,加載完成或者加載失敗的結(jié)果。
boolean onLoadFailed(@Nullable GlideException e, Object model, Target<R> target,
boolean isFirstResource);
boolean onResourceReady(R resource, Object model, Target<R> target, DataSource dataSource,
boolean isFirstResource);
listener方法的參數(shù)RequestListener內(nèi)部有兩個方法
- onResourceReady 標識加載成功俱萍,以及圖片資源resource端壳,返回值標識是否處理了結(jié)果
- onLoadFailed 表示加載失敗,以及失敗的原因GlideException 枪蘑。返回值標識是否處理了結(jié)果
listener方法的參數(shù)RequestListener损谦,會在構(gòu)建Request的時候,保存在request的屬性requestListener岳颇。
在DecodeJob回調(diào)的流程中會在Request中調(diào)用onResourceReady或者onLoadFailed