復現場景
在適配器中添加了一個條件,path爲空就去加載本地圖片,不爲空就使用Glide
去加載圖片,僞代碼實現如下:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if(path==null){
imageView.setImageResource(R.drawable.my_image)
}else{
Glide.with(this).load(path).apply( RequestOptions().placeholder(R.drawable.place).error(R.drawable.error)).into(imageView)
}
}
咋一看沒啥問題,當path爲一個無效地址的時候,imageView會顯示error的圖片,但是當path爲null的時候,發現imageView沒有顯示my_image,還是顯示的error圖片,打斷點發現imageView.setImageResource(R.drawable.my_image)
這句代碼是執行了的,但爲什麼會失效呢?
解決方案
先來說一下解決方案,如果對出現原因(從源碼層面分析)感興趣可以繼續往下看
1.使用Glide.with(this).clear(imageView)
關閉失敗重試
2.使用Glide
加載本地圖片,不使用原生Api:
Glide.with(imageView).load(R.drawable.my_image).into(imageView)
出現原因
Glide內部有加載失敗重試機制,當第一次加載失敗,重試機制就會啓動,這時imageView.setImageResource(R.drawable.my_image)
也同步執行了,但是當重試機制執行完畢後,Glide發現圖片最終還是加載失敗,所以會將error中設置的圖片又添加到imageView
上去,所以並不是imageView.setImageResource(R.drawable.my_image)
這句代碼失效了,而是Glide又重新設置了一遍。
我們可以來瞅一眼源碼,我們知道圖片加載失敗是有一個回調的,所以從回調入手:
先找到RequestListener
回調:
public interface RequestListener<R> {
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);
}
隨後找到onLoadFailed
實現類SingleRequest
:
/**
* A callback method that should never be invoked directly.
*/
@Override
public void onLoadFailed(GlideException e) {
onLoadFailed(e, Log.WARN);
}
private void onLoadFailed(GlideException e, int maxLogLevel) {
stateVerifier.throwIfRecycled();
int logLevel = glideContext.getLogLevel();
if (logLevel <= maxLogLevel) {
Log.w(GLIDE_TAG, "Load failed for " + model + " with size [" + width + "x" + height + "]", e);
if (logLevel <= Log.INFO) {
e.logRootCauses(GLIDE_TAG);
}
}
loadStatus = null;
status = Status.FAILED;
isCallingCallbacks = true;
try {
//TODO: what if this is a thumbnail request?
if ((requestListener == null
|| !requestListener.onLoadFailed(e, model, target, isFirstReadyResource())) //可以看到listence的接口調用是在這裏,返回值決定了是否往下調用邏輯
&& (targetListener == null
|| !targetListener.onLoadFailed(e, model, target, isFirstReadyResource()))) {
setErrorPlaceholder();
}
} finally {
isCallingCallbacks = false;
}
notifyLoadFailed(); //這裏會重試一次
}
可以看到這裏會先有一個requestListener
的接口調用,這裏requestListener
其實就是我們的listence監聽功能:
Glide.with(imageView).load(url).listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
return false;
}
})
只有返回false纔會去調用setErrorPlaceholder()
方法,看一下:
private void setErrorPlaceholder() {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = null;
if (model == null) {
error = getFallbackDrawable();
}
// Either the model isn't null, or there was no fallback drawable set.
if (error == null) {
error = getErrorDrawable();
}
// The model isn't null, no fallback drawable was set or no error drawable was set.
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(error); //target就是我們設置進去的ImageView
}
在看一下我們的onLoadFailed
實現:
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
所以setErrorPlaceholder
方法的作用就是把我們設置的error圖片設置給我們放進去的ImageView上
我們回到onLoadFailed
方法中,發現最後調用了notifyLoadFailed()
方法,這個方法是幹嘛的呢,看一下源碼:
private void notifyLoadFailed() {
if (requestCoordinator != null) {
requestCoordinator.onRequestFailed(this);
}
}
是一個接口,我們看一下它的實現,是在ErrorRequestCoordinator
中:
if (!request.equals(error)) {
if (!error.isRunning()) {
error.begin();
}
return;
}
if (parent != null) {
parent.onRequestFailed(this);
}
我們發現調用了一個熟悉的方法,begin()
,研究過源碼的都知道,Glide都會在這個接口實現開始異步加載的方法,所以在這裏又會去開始異步加載圖片
所以,走一圈下來,大致的流程就是這樣子:
在SingleRequest
類中先調用begin
開始異步加載圖片->加載失敗->onLoadFailed()
->調用notifyLoadFailed()
方法重試(內部會判斷是否需要重試)->需要加載重試->begin