參考:
Glide4用法官方文檔:https://muyangmin.github.io/glide-docs-cn/doc/getting-started.html
Glide3.7.0源碼解析:https://blog.csdn.net/sinyu890807/column/info/15318
《Android面試題:Glide》 https://blog.csdn.net/songzi1228/article/details/84426165
Q:瞭解Glide的實現原理嗎?
A:瞭解過,主要有幾個方面:
- 圖片緩存:內存緩存(LRU緩存+弱引用緩存),磁盤緩存,Bitmap對象池(Bitmap對象複用)
- 請求管理:根據生命週期取消request,重啓失敗request,請求優先級,縮略圖請求管理
- 圖片變換,圖片解碼
零,下載Glide源碼
一,開始分析,Glide常用句式Glide.with(fragment).load(myUrl).into(imageView);
先分析第一步Glide.with(context)
with方法是使用Glide加載圖片的第一步,Glide類中的靜態方法,返回RequestManager對象,根據傳入的Context參數類型不同,主要有5個重載的with方法,區別是圖片請求生命週期不同。
* @see #with(Context)
* @see #with(android.app.Activity)
* @see #with(android.app.Fragment)
* @see #with(androidx.fragment.app.Fragment)
* @see #with(androidx.fragment.app.FragmentActivity)
Glide可以根據當前頁面的生命週期,自動重新開始或暫停或取消結束請求;實現原理就是根據傳入的Context類型,在當前頁面上生成一個看不見的fragment,來獲得當前頁面的生命週期的回調,進行請求相關的操作。如果傳入的是Application,請求的生命週期將和應用一樣長,不會自動取消;如果傳入的是Activity或Fragment,當前Activity或Fragment退出時,請求將會自動終止。所以,從內存優化的角度看,應該傳入當前頁面的Context類型。比如child fragment頁面上的Imagview顯示圖片,應該傳入child fragment,而不是parent fragment和fragment所在的Activity。
可以看看源碼的註釋:
/**
* Begin a load with Glide by passing in a context.
*
* <p>Any requests started using a context will only have the application level options applied
* and will not be started or stopped based on lifecycle events. In general, loads should be
* started at the level the result will be used in. If the resource will be used in a view in a
* child fragment, the load should be started with {@link #with(android.app.Fragment)}} using that
* child fragment. Similarly, if the resource will be used in a view in the parent fragment, the
* load should be started with {@link #with(android.app.Fragment)} using the parent fragment. In
* the same vein, if the resource will be used in a view in an activity, the load should be
* started with {@link #with(android.app.Activity)}}.
*
* <p>This method is appropriate for resources that will be used outside of the normal fragment or
* activity lifecycle (For example in services, or for notification thumbnails).
*
* @param context Any context, will not be retained.
* @return A RequestManager for the top level application that can be used to start a load.
* @see #with(android.app.Activity)
* @see #with(android.app.Fragment)
* @see #with(androidx.fragment.app.Fragment)
* @see #with(androidx.fragment.app.FragmentActivity)
*/
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
所有with方法裏面都是一句話,最終都是1.先調用getRetriever(context)方法獲得RequestManagerRetriever對象,2.再調用RequestManagerRetriever對象的get(context)方法獲得RequestManager對象。
RequestManagerRetriever這個類幹嘛用的呢?源碼裏面的註釋:A collection of static methods for creating new RequestManager or retrieving existing ones from activities and fragment.一些靜態方法的集合,這些方法是用來創建新的requestManager對象或從activities和fragment中取回已存在的requestManager對象。
1.先看getRetriever(context)方法,
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
getRetriever(context)方法裏面通過Glide.get(context)方法獲取Glide單例對象,再調用getRequestManagerRetriever()簡單的將requestManagerRetriever對象返回。在創建Glide單例對象的過程中,會創建並持有一個requestManagerRetriever對象。Glide單例對象在get方法裏創建,這裏先不跟進去看。(TODO:Glide的創建)
2.再看RequestManagerRetriever對象的get(context)方法,get方法和Glide裏面的with方法一樣有多個context子類參數的重載。
* @see #get(Context)
* @see #get(android.app.Activity)
* @see #get(android.app.Fragment)
* @see #get(androidx.fragment.app.Fragment)
* @see #get(androidx.fragment.app.FragmentActivity)
每個方法都看有點眼花,我們先看看其中的get(android.app.Activity)方法的實現
,(TODO:重載的get方法)
/**
* A collection of static methods for creating new {@link com.bumptech.glide.RequestManager}s or
* retrieving existing ones from activities and fragment.
*/
public class RequestManagerRetriever implements Handler.Callback {
......
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
......
}
get(android.app.Activity)裏面,先判斷是否是後臺線程,(如果是後臺線程,請求無需根據activity頁面的銷燬而結束了),就直接調用參數ApplicationContext的get(Context)方法,創建一個和Application生命週期一樣長的請求。如果是主線程,調用了fragmentGet方法並返回。
那麼看看fragmenGet()方法:
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
fragmenGet()方法裏面,調用getRequestManagerFragment返回了一個fragment對象RequestManagerFragment,並創建一個RequestManager與fragment對象綁定,最後返回了RequestManager。
那麼看看getRequestManagerFragment()方法:
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
getRequestManagerFragment()方法裏面,new RequestManagerFragment,並通過傳入的FragmentManager添加到了頁面上。從這裏可以看出,RequestManagerFragment是根據頁面的生命週期來管理Glide request請求的關鍵人物了。
那麼看看RequestManagerFragment類裏有什麼,(可以源碼的類的註釋,一般都很清楚地描述了類的功能)
/**
* A view-less {@link android.app.Fragment} used to safely store an {@link
* com.bumptech.glide.RequestManager} that can be used to start, stop and manage Glide requests
* started for targets the fragment or activity this fragment is a child of.
*
* @see com.bumptech.glide.manager.SupportRequestManagerFragment
* @see com.bumptech.glide.manager.RequestManagerRetriever
* @see com.bumptech.glide.RequestManager
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public class RequestManagerFragment extends Fragment {
......
private final ActivityFragmentLifecycle lifecycle;
private RequestManager requestManager;
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
public void setRequestManager(@Nullable RequestManager requestManager) {
this.requestManager = requestManager;
}
public RequestManager getRequestManager() {
return requestManager;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
registerFragmentWithRoot(activity);
} catch (IllegalStateException e) {
// OnAttach can be called after the activity is destroyed, see #497.
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to register fragment with root", e);
}
}
}
@Override
public void onDetach() {
super.onDetach();
unregisterFragmentWithRoot();
}
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
private void registerFragmentWithRoot(@NonNull Activity activity) {
unregisterFragmentWithRoot();
rootRequestManagerFragment =
Glide.get(activity).getRequestManagerRetriever().getRequestManagerFragment(activity);
if (!equals(rootRequestManagerFragment)) {
rootRequestManagerFragment.addChildRequestManagerFragment(this);
}
}
private void unregisterFragmentWithRoot() {
if (rootRequestManagerFragment != null) {
rootRequestManagerFragment.removeChildRequestManagerFragment(this);
rootRequestManagerFragment = null;
}
}
......
}
RequestManagerFragment類繼承Fragemt類,在onAttach,onDetach方法中分別調用了registerFragmentWithRoot,unregisterFragmentWithRoot實現子fragment的添加和移除。RequestManagerFragment的構造方法中new ActivityFragmentLifecycle對象,並且在onStart,onStop,onDestroy中分別調用了ActivityFragmentLifecycle對象的onStart,onStop,onDestroy方法,看來就是通過ActivityFragmentLifecycle保存和通知頁面的生命週期狀態的變化。
那麼看看ActivityFragmentLifecycle類,
/**
* A {@link com.bumptech.glide.manager.Lifecycle} implementation for tracking and notifying
* listeners of {@link android.app.Fragment} and {@link android.app.Activity} lifecycle events.
*/
class ActivityFragmentLifecycle implements Lifecycle {
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;
/**
* Adds the given listener to the list of listeners to be notified on each lifecycle event.
*
* <p>The latest lifecycle event will be called on the given listener synchronously in this
* method. If the activity or fragment is stopped, {@link LifecycleListener#onStop()}} will be
* called, and same for onStart and onDestroy.
*
* <p>Note - {@link com.bumptech.glide.manager.LifecycleListener}s that are added more than once
* will have their lifecycle methods called more than once. It is the caller's responsibility to
* avoid adding listeners multiple times.
*/
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
@Override
public void removeListener(@NonNull LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}
ActivityFragmentLifecycle類很簡單,一個set集合接收註冊監聽,並在onStart,onStop,onDestroy方法中,通知所有註冊監聽類。
好了,with方法的跟蹤就到此爲止,總結下里面主要的幾個事:
1.怎麼創建Glide單例對象,
2.怎麼實現對頁面生命週期事件的響應(創建fragment到頁面並與創建的requestManager綁定)。
二,再分析第二步load(myUrl)
通過上面的分析,我們知道with返回了一個RequestManager對象,現在看看RequestManager對象的load方法,
/**
* A class for managing and starting requests for Glide. Can use activity, fragment and connectivity
* lifecycle events to intelligently stop, start, and restart requests. Retrieve either by
* instantiating a new object, or to take advantage built in Activity and Fragment lifecycle
* handling, use the static Glide.load methods with your Fragment or Activity.
*
* @see Glide#with(android.app.Activity)
* @see Glide#with(androidx.fragment.app.FragmentActivity)
* @see Glide#with(android.app.Fragment)
* @see Glide#with(androidx.fragment.app.Fragment)
* @see Glide#with(Context)
*/
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
......
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Bitmap)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Drawable)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(String)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Uri)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(File)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(Integer)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(URL)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(byte[])}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
/**
* A helper method equivalent to calling {@link #asDrawable()} and then {@link
* RequestBuilder#load(Object)} with the given model.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
}
從源碼看出,load()方法的重載的方法真是多,接收不同的參數類型(Bitmap,Drawable,String,Uri,Integer等等),但裏面都是一句話:1.先調用asDrawable方法返回RequestBuilder對象,2.再調用RequestBuilder對象的load方法,並將接收的參數直接傳過去(說明load方法也是重載)。
1.那麼看看asDrawable方法,(看方法註釋,真是寫的非常清楚)
/**
* Attempts to always load the resource using any registered {@link
* com.bumptech.glide.load.ResourceDecoder}s that can decode any subclass of {@link Drawable}.
*
* <p>By default, may return either a {@link android.graphics.drawable.BitmapDrawable} or {@link
* GifDrawable}, but if additional decoders are registered for other {@link Drawable} subclasses,
* any of those subclasses may also be returned.
*
* @return A new request builder for loading a {@link Drawable}.
*/
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
asDrawable裏面調用as方法,傳入Drawable.class;記住這個Drawable.class,後面它將傳遞個RequestBuilder的泛型,代表資源類型。
看看as方法,(多看看註釋)
/**
* Attempts to load the resource using any registered {@link
* com.bumptech.glide.load.ResourceDecoder}s that can decode the given resource class or any
* subclass of the given resource class.
*
* @param resourceClass The resource to decode.
* @return A new request builder for loading the given resource class.
*/
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
as方法的註釋:根據傳入的資源類型返回一個request builder 去加載指定的資源類型。
前面傳入的參數是Drawable.class,所以返回了RequestBuilder<Drawable>。
2.再調用RequestBuilder對象的load方法,那麼看看RequestBuilder類的load方法,
RequestBuilder類的註釋:一個泛型類,可以處理泛型資源類型的設置選項和啓動負載。泛型爲將要傳遞給Target的資源類型。
/**
* A generic class that can handle setting options and staring loads for generic resource types.
*
* @param <TranscodeType> The type of resource that will be delivered to the {@link
* com.bumptech.glide.request.target.Target}.
*/
// Public API.
@SuppressWarnings({"unused", "WeakerAccess"})
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
......
/**
* Returns a request builder to load the given {@link java.lang.String}.
*
* <p>Note - this method caches data using only the given String as the cache key. If the data is
* a Uri outside of your control, or you otherwise expect the data represented by the given String
* to change without the String identifier changing, Consider using {@link
* com.bumptech.glide.request.RequestOptions#signature(com.bumptech.glide.load.Key)} to mixin a
* signature you create that identifies the data currently at the given String that will
* invalidate the cache if that data changes. Alternatively, using {@link
* com.bumptech.glide.load.engine.DiskCacheStrategy#NONE} and/or {@link
* com.bumptech.glide.request.RequestOptions#skipMemoryCache(boolean)} may be appropriate.
*
* @see #load(Object)
* @param string A file path, or a uri or url handled by {@link
* com.bumptech.glide.load.model.UriLoader}.
*/
@NonNull
@Override
@CheckResult
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
return loadGeneric(uri);
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable File file) {
return loadGeneric(file);
}
@Override
public RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return loadGeneric(resourceId).apply(signatureOf(AndroidResourceSignature.obtain(context)));
}
@Override
public RequestBuilder<TranscodeType> load(@Nullable byte[] model) {
RequestBuilder<TranscodeType> result = loadGeneric(model);
if (!result.isDiskCacheStrategySet()) {
result = result.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
}
if (!result.isSkipMemoryCacheSet()) {
result = result.apply(skipMemoryCacheOf(true /*skipMemoryCache*/));
}
return result;
}
@Nullable private Object model;
private boolean isModelSet;
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
......
}
load(String url)方法的註釋:此方法僅使用給定字符串作爲緩存鍵來緩存數據。如果數據是一個超出您控制範圍的Uri,或者您期望由給定字符串表示的數據,若要在不更改字符串標識符的情況下進行更改,請考慮使用RequestOptions#signature(Key)來混合您創建的用於標識當前位於給定字符串處的數據的簽名,如果數據更改,則使緩存無效。或者,使DiskCacheStrategy#NONE或RequestOptions.skipMemoryCache(boolean)可能是合適的。
簡單地說就是,load方法接收的參數,會被作爲緩存的key,當然我們也可以自定義key,使用RequestOptions#signature(Key)。(TODO:後面再說緩存的key)
我們看到所有的load方法,最後都是調用了下loadGeneric方法,並傳入接收的參數。而loadGenric方法只有一個,參數類型爲Object,方法裏面用model存儲了接收的參數,並返回RequestBuilder自身。那麼也就是說,第三步into方法,也是在RequestBuilder類裏了。
總結下第二步load方法做的事情:就一個,Object model成員變量存入load方法接收的不同類型參數。
(load方法基本啥也沒幹?有的懷疑是不是搞錯了,去看了郭霖Glide3.7的源碼解析,果然和4.12不一樣,改版了)
第三步,再分析第三步into(imageView)
直接看RequestBuilder的into方法,(還是可以先看看方法的註釋)
/**
* Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
* the view, and frees any resources Glide may have previously loaded into the view so they may be
* reused.
*
* @see RequestManager#clear(Target)
* @param view The view to cancel previous loads for and load the new resource into.
* @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link
* ImageView}.
*/
@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
第一行會判斷是否在主線程,不是的話拋出異常,所以into方法必須在主線程調用。後面根據ImageView的ScaleType設置了requestionOptions,看樣子是對圖片做響應的縮放設置。
最後一句,幹了兩件事:1.將ImageView包裝成Target,2.調用的另一個into方法。
下面分別看看:
1.將ImageView包裝成Target
看看glideContext的buildImageViewTarget方法,
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
繼續跟進到imageViewTargetFactory.buildTarget(),
/**
* A factory responsible for producing the correct type of {@link
* com.bumptech.glide.request.target.Target} for a given {@link android.view.View} subclass.
*/
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
工廠類ImageViewTargetFactory ,會根據第二個參數類型,也就是創建RequestBuilder時的泛型類型(代表資源類型),來決定將ImageView包裝成BitmapImageViewTarget或者DrawableImageViewTarget。
BitmapImageViewTarget和DrawableImageViewTarget都是ImageViewTarget的子類,主要區別就一個方法不一樣。
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
......
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
......
}
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
......
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
......
}
那麼看看關鍵的父類ImageViewTarget,這個類很簡單
**
* A base {@link com.bumptech.glide.request.target.Target} for displaying resources in {@link
* android.widget.ImageView}s.
*
* @param <Z> The type of resource that this target will display in the wrapped {@link
* android.widget.ImageView}.
*/
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
implements Transition.ViewAdapter {
@Nullable private Animatable animatable;
public ImageViewTarget(ImageView view) {
super(view);
}
/** @deprecated Use {@link #waitForLayout()} instead. */
@SuppressWarnings({"deprecation"})
@Deprecated
public ImageViewTarget(ImageView view, boolean waitForLayout) {
super(view, waitForLayout);
}
/**
* Returns the current {@link android.graphics.drawable.Drawable} being displayed in the view
* using {@link android.widget.ImageView#getDrawable()}.
*/
@Override
@Nullable
public Drawable getCurrentDrawable() {
return view.getDrawable();
}
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param drawable {@inheritDoc}
*/
@Override
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param placeholder {@inheritDoc}
*/
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param errorDrawable {@inheritDoc}
*/
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
setResourceInternal(null);
setDrawable(errorDrawable);
}
/**
* Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* @param placeholder {@inheritDoc}
*/
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
super.onLoadCleared(placeholder);
if (animatable != null) {
animatable.stop();
}
setResourceInternal(null);
setDrawable(placeholder);
}
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
@Override
public void onStart() {
if (animatable != null) {
animatable.start();
}
}
@Override
public void onStop() {
if (animatable != null) {
animatable.stop();
}
}
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
private void maybeUpdateAnimatable(@Nullable Z resource) {
if (resource instanceof Animatable) {
animatable = (Animatable) resource;
animatable.start();
} else {
animatable = null;
}
}
protected abstract void setResource(@Nullable Z resource);
}
ImageViewTarget持有ImageView,所以給ImageView設置圖片的方法都是在這裏了:
- onLoadStarted時,設置顯示圖片爲佔位圖placeholder
- onLoadFailed時,設置顯示圖片爲錯誤圖errorDrawable
- onResourceReady,也就是資源準備好了,設置到Imageview上或者開始動畫
- onStart/onStop,判斷爲動畫則進行動畫開始/停止
簡單看了看ImageViewTarget的父類ViewTarget,不貼代碼了,得到一個關鍵信息:ViewTarget會持有request,通過ImageView的setTag方法存儲對應的request對象。
2.調用的另一個into方法
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
//給target設置request前,先清楚target之前得request和相關監聽類
requestManager.clear(target);
//request和target綁定,也就是和ImageView綁定
target.setRequest(request);
//開始跟蹤request
requestManager.track(target, request);
return target;
}
這裏面創建了一個Request對象,如果target已經有request並且和當前創建的request相同的話,則使用舊的request開始請求。
這裏我們關注倒數第二句, requestManager.track(target, request);
跟進看看 requestManager的track方法,
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
......
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
......
}
好,這裏有出現兩個Tracker類:
1.TargetTracker類:保存當前所有正在使用的target,並傳遞生命週期事件,控制target的start,stop,destroy等。
2.RequestTracker類:A class for tracking, canceling, and restarting in progress, completed, and failed requests.
TargetTracker類的track方法只是將target添加到set集合,下面我們看看RequestTracker類的runRequest方法:
public class RequestTracker {
......
/** Starts tracking the given request. */
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
......
}
好了,我們的request請求,就是在這裏begin開始執行了,如果它不是暫停狀態。
Request請求裏面都是怎麼執行的呢?點擊進去發現,Request是個接口,那麼需要回頭看看創建request是什麼了。
into方法裏面的調用了buildRequest方法,buildRequest調用了buildRequestRecursive,看看buildRequestRecursive,
private Request buildRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
Request mainRequest =
buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest =
errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
這裏有三個關鍵對象,mainRquest,errorRequest,errorRequestCoordinator,猜測errorRequestCoordinator應該是協調mainRquest與errorRequest。這裏我們先不看。(TODO:errorRequestCoordinator的實現原理)
先看看創建mainRequest的方法buildThumbnailRequestRecursive,
private Request buildThumbnailRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
if (thumbnailBuilder != null) {
// Recursive case: contains a potentially recursive thumbnail request builder.
if (isThumbnailBuilt) {
throw new IllegalStateException(
"You cannot use a request as both the main request and a "
+ "thumbnail, consider using clone() on the request(s) passed to thumbnail()");
}
TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =
thumbnailBuilder.transitionOptions;
// Apply our transition by default to thumbnail requests but avoid overriding custom options
// that may have been applied on the thumbnail request explicitly.
if (thumbnailBuilder.isDefaultTransitionOptionsSet) {
thumbTransitionOptions = transitionOptions;
}
Priority thumbPriority =
thumbnailBuilder.isPrioritySet()
? thumbnailBuilder.getPriority()
: getThumbnailPriority(priority);
int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();
int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !thumbnailBuilder.isValidOverride()) {
thumbOverrideWidth = requestOptions.getOverrideWidth();
thumbOverrideHeight = requestOptions.getOverrideHeight();
}
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest =
thumbnailBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
coordinator,
thumbTransitionOptions,
thumbPriority,
thumbOverrideWidth,
thumbOverrideHeight,
thumbnailBuilder,
callbackExecutor);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
BaseRequestOptions<?> thumbnailOptions =
requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest =
obtainRequest(
requestLock,
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight,
callbackExecutor);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
和前面的方法有的像,這裏也有個關鍵對象ThumbnailRequestCoordinator,應該是協調正常request與thumbRequest,這裏我們仍然先忽略,(TODO:ThumbnailRequestCoordinator的實現原理)只看最後一句,也就是Base case: no thumbnail.
那麼進入obtainRequest方法看看,
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
看到這裏調用了SinglRequest的obtain方法獲取單例對象,好了,找到了Request的具體實現類了SingleRequest,那麼看看SingleRequest的begin方法,
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting
// a new load etc. This does mean that users who want to restart a load because they expect
// that the view size has changed will need to explicitly clear the View or Target before
// starting the new load.
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
}
粗略一看,我以爲關鍵的一句是 倒數第二個if:target.onLoadStarted(getPlaceholderDrawable()); 在加載請求前給target設置佔位圖placeholder。那圖片的請求在哪裏???難道在前面計算size的方法裏面?
倒數第三個if:先判斷overWidth和overHeight是否是有效的(寬高都大於0或者等於圖片的原始寬高),否則target.getSize方法中計算好width和height後,通過回調接口,還是調用了onSizeReady方法。
那麼看看onSizeReady方法,
/** A callback method that should never be invoked directly. */
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
}
調用了engine的load方法,並傳入了簽名,寬高和各種requestOption配置(緩存,變化等),感覺很近網絡加載了。
進入load方法,
load方法的註釋,說明了請求的機制:一個資源會先從當前正在使用的資源中尋找,找到返回並將所有新的非正在使用資源移到內存緩存中,找不到去cache緩存中找,找到添加到當前正在使用的資源集合中並返回,沒找到則先檢查當前正在進行的加載請求有沒有,有的話添加一個回調,沒有的話開始一個新的加載請求。
/**
* Starts a load for the given arguments.
*
* <p>Must be called on the main thread.
*
* <p>The flow for any request is as follows:
*
* <ul>
* <li>Check the current set of actively used resources, return the active resource if present,
* and move any newly inactive resources into the memory cache.
* <li>Check the memory cache and provide the cached resource if present.
* <li>Check the current set of in progress loads and add the cb to the in progress load if one
* is present.
* <li>Start a new load.
* </ul>
*
* <p>Active resources are those that have been provided to at least one request and have not yet
* been released. Once all consumers of a resource have released that resource, the resource then
* goes to cache. If the resource is ever returned to a new consumer from cache, it is re-added to
* the active resources. If the resource is evicted from the cache, its resources are recycled and
* re-used if possible and the resource is discarded. There is no strict requirement that
* consumers release their resources so active resources are held weakly.
*
* @param width The target width in pixels of the desired resource.
* @param height The target height in pixels of the desired resource.
* @param cb The callback that will be called when the load completes.
*/
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
load方法裏面,先生成EngineKey對象,拿着key,先去內存中找,找到回調出去,沒找到則調用waitForExistingOrStartNewJob,顧名思義,等待一個已存在的請求或者開始一個新的請求。
繼續看waitForExistingOrStartNewJob,
private <R> LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor,
EngineKey key,
long startTime) {
//從正在執行中的jobs查找是否已存在
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
//存在的話,給EngineJob添加回調
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
//存儲當前的engineJob
jobs.put(key, engineJob);
//添加回調
engineJob.addCallback(cb, callbackExecutor);
//開始請求
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
這裏創建了EngineJob和DecodeJob,並調用了engineJob的start方法,開始執行decodeJob.
/**
* A class that manages a load by adding and removing callbacks for for the load and notifying
* callbacks when the load completes.
*/
class EngineJob<R> implements DecodeJob.Callback<R>, Poolable {
......
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
......
}
GlideExecutor線程池執行decodeJob,那麼我們來看看decodeJob的run方法。
/**
* A class responsible for decoding resources either from cached data or from the original source
* and applying transformations and transcodes.
*
* <p>Note: this class has a natural ordering that is inconsistent with equals.
*
* @param <R> The type of resource that will be transcoded from the decoded and transformed
* resource.
*/
class DecodeJob<R>
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
......
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
......
}
run方法調用了runWrapped方法。runWrapped流程:
1.先調先調用了getNextStage(Stage.INITIALIZE)方法獲取到當前的stage;
2.再調用getNextGenerator方法,getNextGenerator方法會根據stage獲取到對應的DataFetcherGenerator對象(ResourceCacheGenerator/DataCacheGenerator/SourceGenerator);
3.最後調用runGenerators方法,裏面執行了DataFetcherGenerator對象的startNext方法。startNext方法返回值如果是true就不會執行while語句塊裏面,否則獲取下一個狀態getNextStage,getNextGenerator, 如果stage == Stage.SOURCE調用reschedule重新執行job。
這裏的關注點是第三步裏面執行了DataFetcherGenerator對象的startNext方法,這裏負責獲取數據,從緩存或者網絡上。
如果是從網絡加載,會調用SourceGenerator對象的startNext方法:
/**
* Generates {@link com.bumptech.glide.load.data.DataFetcher DataFetchers} from original source data
* using registered {@link com.bumptech.glide.load.model.ModelLoader ModelLoaders} and the model
* provided for the load.
*
* <p>Depending on the disk cache strategy, source data may first be written to disk and then loaded
* from the cache file rather than returned directly.
*/
class SourceGenerator implements DataFetcherGenerator, DataFetcherGenerator.FetcherReadyCallback {
......
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
startNextLoad(loadData);
}
}
return started;
}
private void startNextLoad(final LoadData<?> toStart) {
//這裏的fetcher是HttpUrlFetcher,真正發起網絡請求獲取數據的類。
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
......
}
先看看SourceGenerator類的註釋:
*從原始源數據生成DataFetcher,使用註冊的ModelLoader和爲load提供model。
*<p>根據磁盤緩存策略,源數據可能首先寫入磁盤,然後加載,而不是直接返回。
DaraFetcher是實際獲取數據的類,ModelLoader是幹嘛的?LoadData又是幹嘛的?
1.ModelLoader的註釋:
*一種工廠接口,用於將任意複雜的數據模型轉換爲具體的數據類型,DataFetcher使用該數據類型從model獲取數據。
*<p>這不僅可以避免在xml和代碼中重複維度以確定具有不同密度的設備上的視圖大小,還允許您使用佈局權重或以編程方式放置視圖的維度,而不必強制獲取通用資源大小。
*<p>獲取的資源越小,使用的帶寬和電池壽命就越短,每個資源的內存佔用就越少。
*@param<Model>模型的類型。
*@param<Data>可以由{@link使用的數據類型
*com.bumptech.glide.load.resourcedodecoder}解碼資源。
2.LoadData : ModelLoader的內部類,包含一組識別加載資源的Key、指向相同數據的備用緩存鍵和一個DataFetcher可用於獲取緩存中未找到的數據。
3.model: 是我們調用load方法時,傳入的參數。