Glide設定預設圖片後setImageBitmap,setImageResource失效問題
復現場景
在介面卡中添加了一個條件,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