Android源碼進階之Glide加載流程和源碼詳解
本文轉載自微信公眾號「Android開發編程」,作者Android開發編程。轉載本文請聯系Android開發編程公眾號。
前言
Glide是純Java寫的Android端開源圖片加載庫,能夠幫助我們下載、緩存、展示多種格式圖片,也包括GIF格式;
昨天我們從源碼里分析了,glide的緩存策略機制;
那今天我們就趁熱打鐵來分析一波加載流程;
一、glide常用的加載方法
1、加載圖片到imageView
- Glide.with(Context context).load(Strint url).into(ImageView imageView);
2、各種形式的圖片加載到ImageView
- // 加載本地圖片
- File file = new File(getExternalCacheDir() + "/image.jpg");
- Glide.with(this).load(file).into(imageView);
- // 加載應用資源
- int resource = R.drawable.image;
- Glide.with(this).load(resource).into(imageView);
- // 加載二進制流
- byte[] image = getImageBytes();
- Glide.with(this).load(image).into(imageView);
- // 加載Uri對象
- Uri imageUri = getImageUri();
- Glide.with(this).load(imageUri).into(imageView);
3、加載帶有占位圖
- Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView);
占位圖目的為在目的圖片還未加載出來的時候,提前展示給用戶的一張圖片;
4、加載失敗 放置占位符
- Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)//關閉Glide的硬盤緩存機制
- .into(imageView);
- //DiskCacheStrategy.NONE:表示不緩存任何內容。
- //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。
- //DiskCacheStrategy.RESULT:表示只緩存轉換過后的圖片(默認選項)。
- //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉換過后的圖片。
5、加載指定格式的圖片--指定為靜止圖片
- Glide.with(this)
- .load(url)
- .asBitmap()//只加載靜態圖片,如果是git圖片則只加載第一幀。
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .into(imageView);
6、加載動態圖片
- Glide.with(this)
- .load(url)
- .asGif()//加載動態圖片,若現有圖片為非gif圖片,則直接加載錯誤占位圖。
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .into(imageView);
7、加載指定大小的圖片
- Glide.with(this)
- .load(url)
- .placeholder(R.drawable.loading)
- .error(R.drawable.error)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .override(100, 100)//指定圖片大小
- .into(imageView);
8、關閉框架的內存緩存機制
- Glide.with(this)
- .load(url)
- .skipMemoryCache(true) //傳入參數為false時,則關閉內存緩存。
- .into(imageView);
9、關閉硬盤的緩存
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.NONE) //關閉硬盤緩存操作
- .into(imageView);
- //其他參數表示:
- //DiskCacheStrategy.NONE:表示不緩存任何內容。
- //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。
- //DiskCacheStrategy.RESULT:表示只緩存轉換過后的圖片(默認選項)。
- //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉換過后的圖片。
10、當引用的 url 存在 token 時解決方法
- public class MyGlideUrl extends GlideUrl {
- private String mUrl;
- public MyGlideUrl(String url) {
- super(url);
- mUrl = url;
- }
- @Override
- public String getCacheKey() {
- return mUrl.replace(findTokenParam(), "");
- }
- private String findTokenParam() {
- String tokenParam = "";
- int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
- if (tokenKeyIndex != -1) {
- int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
- if (nextAndIndex != -1) {
- tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
- } else {
- tokenParam = mUrl.substring(tokenKeyIndex);
- }
- }
- return tokenParam;
- }
- }
然后加載圖片的方式為:
- Glide.with(this)
- .load(new MyGlideUrl(url))
- .into(imageView);
11、利用Glide將圖片加載到不同控件或加載成不同使用方式
(1)、拿到圖片實例
- //1、通過自己構造 target 可以獲取到圖片實例
- SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
- @Override
- public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
- imageView.setImageDrawable(resource);
- }
- };
- //2、將圖片實例記載到指定的imageview上,也可以做其他的事情
- 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);
- }
(2)、將圖片加載到任何位置
- /*
- *將圖片加載為控件背景
- */
- public class MyLayout extends LinearLayout {
- private ViewTarget<MyLayout, GlideDrawable> viewTarget;
- public MyLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
- @Override
- public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
- MyLayout myLayout = getView();
- myLayout.setImageAsBackground(resource);
- }
- };
- }
- public ViewTarget<MyLayout, GlideDrawable> getTarget() {
- return viewTarget;
- }
- public void setImageAsBackground(GlideDrawable resource) {
- setBackground(resource);
- }
- }
- //引用圖片到指定控件作為背景
- public class MainActivity extends AppCompatActivity {
- MyLayout myLayout;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- myLayout = (MyLayout) findViewById(R.id.background);
- }
- 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(myLayout.getTarget());
- }
- }
12、Glide 實現預加載
- //a、預加載代碼
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
- .preload();
- //preload() 有兩種重載
- // 1、帶有參數的重載,參數作用是設置預加載的圖片大小;
- //2、不帶參數的表示加載的圖片為原始尺寸;
- //b、使用預加載的圖片
- Glide.with(this)
- .load(url)
- .diskCacheStrategy(DiskCacheStrategy.SOURCE)
- .into(imageView);
二、Glide加載流程詳解
1、with(context)
- // Glide.java
- public static RequestManager with(@NonNull Context context) {
- return getRetriever(context).get(context);
- }
- public static RequestManager with(@NonNull Activity activity) {
- return getRetriever(activity).get(activity);
- }
- public static RequestManager with(@NonNull FragmentActivity activity) {
- return getRetriever(activity).get(activity);
- }
- public static RequestManager with(@NonNull Fragment fragment) {
- return getRetriever(fragment.getContext()).get(fragment);
- }
- public static RequestManager with(@NonNull View view) {
- return getRetriever(view.getContext()).get(view);
- }
該函數創建了Glide實例并初始化了一些基本參數,然后創建了一個RequestManager對象并返回。總共有5個場景,這里就先選取參數為Context類型情形進行分析。
- // Glide.java
- public static RequestManager with(@NonNull Context context) {
- return getRetriever(context).get(context);
- }
可以看到該函數首先調用了getRetriever(context)獲取到了RequestManagerRetriever對象。在創建該對象之前首先通過Glide.java中的get方法獲得了Glide實例(Glide是一個單例),同時讀取AppGlideModule和AndroidManifest.xml的配置。
- // Glide.java
- private static RequestManagerRetriever getRetriever(@Nullable Context context) {
- // Glide.get(context)獲取Glide實例
- return Glide.get(context).getRequestManagerRetriever();
- }
- public static Glide get(@NonNull Context context) {
- if (glide == null) {
- // 加載AppGlideModule
- GeneratedAppGlideModule annotationGeneratedModule =
- getAnnotationGeneratedGlideModules(context.getApplicationContext());
- synchronized (Glide.class) {
- if (glide == null) {
- // 加載Mainfest配置、注冊模塊回調
- // 這一步執行了 Glide.build()方法構造Glide實例。build方法下面會講到
- checkAndInitializeGlide(context, annotationGeneratedModule);
- }
- }
- }
- return glide;
- }
獲取到Glide實例后,緊接著調用getRequestManagerRetriever方法返回了上一步已經初始化好的RequestManagerRetriever對象。
- // Glide.java
- public RequestManagerRetriever getRequestManagerRetriever() {
- return requestManagerRetriever;
- }
接著再看一看RequestManagerRetriever是如何被初始化的,以及初始化過程中都干了哪些事。首先貼源碼看看Glide.build方法內部具體實現(該方法在上述checkAndInitializeGlide()函數中被調用):
- // GlideBuilder.java
- Glide build(@NonNull Context context) {
- // 分配線程池、配置緩存策略
- sourceExecutor = GlideExecutor.newSourceExecutor();
- diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
- animationExecutor = GlideExecutor.newAnimationExecutor();
- memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
- // 監聽網絡變化
- connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
- int size = memorySizeCalculator.getBitmapPoolSize();
- if (size > 0) {
- bitmapPool = new LruBitmapPool(size);
- } else {
- bitmapPool = new BitmapPoolAdapter();
- }
- arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
- memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
- diskCacheFactory = new InternalCacheDiskCacheFactory(context);
- // engine是負責執行加載任務的
- if (engine == null) {
- engine =
- new Engine(
- memoryCache,
- diskCacheFactory,
- diskCacheExecutor,
- sourceExecutor,
- GlideExecutor.newUnlimitedSourceExecutor(),
- animationExecutor,
- isActiveResourceRetentionAllowed);
- }
- if (defaultRequestListeners == null) {
- defaultRequestListeners = Collections.emptyList();
- } else {
- defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
- }
- RequestManagerRetriever requestManagerRetriever =
- new RequestManagerRetriever(requestManagerFactory);
- return new Glide(
- context,
- engine,
- memoryCache,
- bitmapPool,
- arrayPool,
- requestManagerRetriever,
- connectivityMonitorFactory,
- logLevel,
- defaultRequestOptionsFactory,
- defaultTransitionOptions,
- defaultRequestListeners,
- isLoggingRequestOriginsEnabled,
- isImageDecoderEnabledForBitmaps);
- }
- 執行Glide.get()方法時就已經分配好了資源加載、緩存線程池、配置好了緩存策略,這里的engine專門負責加載、解碼資源,ConnectivityMonitor注冊了網絡狀態監聽器,當網絡斷開時暫停請求網絡資源,重連后繼續請求資源;
- RequestManagerRetriever是原來是通過RequestManagerFactory工廠類構造的。進入到RequestManagerFactory.java類中,可以看到get方法獲取到了相應的RequestManager對象;
- 從這里我們可以發現,無論哪種情況,當App進入后臺后會導致頁面不可見,此時RequestManager綁定到了ApplicationContext,與App的生命周期一致,因此在RequestManager.java類中也實現了生命周期相關的回調函數;
- // RequestManagerRetriever.java
- // get有好幾個重載方法,這里僅選取context參數進行分析
- public RequestManager get(@NonNull Context context) {
- if (Util.isOnMainThread() && !(context instanceof Application)) {
- if (context instanceof FragmentActivity) {
- return get((FragmentActivity) context);
- } else if (context instanceof Activity) {
- return get((Activity) context);
- } else if (context instanceof ContextWrapper
- && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
- return get(((ContextWrapper) context).getBaseContext());
- }
- }
- return getApplicationManager(context);
- }
執行完Glide.with(context)后我們拿到了一個對應的RequestManager對象,接下來就執行下一個任務load(url);
2、load(url)
拿到了RequestManager,緊接著調用load方法開始執行下一步操作,同樣先看看load方法的實現
- // RequestManager.java
- public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
- return asDrawable().load(bitmap);
- }
- public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
- return asDrawable().load(drawable);
- }
- public RequestBuilder<Drawable> load(@Nullable String string) {
- return asDrawable().load(string);
- }
- public RequestBuilder<Drawable> load(@Nullable Uri uri) {
- return asDrawable().load(uri);
- }
- public RequestBuilder<Drawable> load(@Nullable File file) {
- return asDrawable().load(file);
- }
- public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
- return asDrawable().load(resourceId);
- }
- public RequestBuilder<Drawable> load(@Nullable URL url) {
- return asDrawable().load(url);
- }
- public RequestBuilder<Drawable> load(@Nullable byte[] model) {
- return asDrawable().load(model);
- }
- public RequestBuilder<Drawable> load(@Nullable Object model) {
- return asDrawable().load(model);
- }
- load()同樣有多個重載函數,傳入的參數可以是圖片對象Bitmap、Drawable、本地資源Uri、在線資源路徑Url、文件對象File、assets資源的id,這里我們只看參數為Url的情形;
- asDrawable().load(url)返回了一個RequestBuilder對象,首先看看asDrawable方法干了什么;
- // RequestManager.java
- public RequestBuilder<Drawable> asDrawable() {
- return as(Drawable.class);
- }
- public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {
- return new RequestBuilder<>(glide, this, resourceClass, context);
- }
asDrawable方法創建了RequestBuilder對象,然后調用RequestBuilder.java中的load方法;
- // RequestBuilder.java
- // 傳入的String類型的url將會被作為緩存的key
- public RequestBuilder<TranscodeType> load(@Nullable String string) {
- return loadGeneric(string);
- }
- // 這里返回了自身
- private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
- this.model = model;
- isModelSet = true;
- return this;
- }
load函數主要工作就是根據傳入的資源類型,構造了一個相應的RequestBuilder對象。至此一切準備工作準備就緒,接下來就是最為重要的一步了-加載、展示文件,讓我們來著看into(view)方法如何完成這些任務;
3、into(view)
拿到的是對應類型RequestBuilder實例,那么就看看該類里into方法的具體實現。同樣into方法有into(@NonNull Y target)和into(@NonNull ImageView )兩個重載函數(這兩個函數最終都會走到同一個函數中),由于調用into方法時我們傳入的參數是ImageView類型的;
- // RequestBuilder.java
- public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
- Util.assertMainThread();
- BaseRequestOptions<?> requestOptions = this;
- // View's scale type.
- // 處理圖片縮放,根據縮放類型來初始化對應的requestOptions對象
- ......
- return into(
- glideContext.buildImageViewTarget(view, transcodeClass),
- /*targetListener=*/ null,
- requestOptions,
- Executors.mainThreadExecutor() // 運行在主線程的handler
- );
- }
- 上面代碼段首先處理圖片縮放類型(裁剪、對齊方式等),并將生成的相關參數放入了requestOptions對象中,然后再將其作為參數傳給了RequestBuilder.java類私有方法into。該方法定義的四個參數分別為:viewTarget、target回調監聽器、請求參數、主線程的回調函數;
- 顯然外部傳入ImageView對象最終被轉換成了ViewTarget對象,轉換函數便是glideContext.buildImageViewTarget(view, transcodeClass);
- // GlideContext.java
- public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
- return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
- }
ViewTarget又是由ImageViewTargetFactory工廠方法生成,接著再看buildTarget方法是如何生成ViewTarget對象。
- // imageViewTargetFactory.java
- public class ImageViewTargetFactory {
- 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)");
- }
- }
- }
可以看到無論傳入參數是何種類型,最終都會轉換成兩種類型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;這里如何選擇取決于asBitmap()、asGif()和asDrawable()函數是否被調用,默認是Bitmap類型,所以這里默認返回的是BitmapImageViewTarget;
- // BitmapImageViewTarget.java
- public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
- public BitmapImageViewTarget(ImageView view) {
- super(view);
- }
- @Override
- protected void setResource(Bitmap resource) {
- view.setImageBitmap(resource); // 顯示圖片
- }
- }
至此ViewTarget創建完畢,我們再回到RequestBuilder.java私有into方法
- // RequestBuilder.java`
- private <Y extends Target<TranscodeType>> Y into(
- @NonNull Y target,
- @Nullable RequestListener<TranscodeType> targetListener,
- BaseRequestOptions<?> options,
- Executor callbackExecutor) {
- // 注釋1:創建request
- Request request = buildRequest(target, targetListener, options, callbackExecutor);
- // 獲取前一個reqeust請求對象
- Request previous = target.getRequest();
- // 與上一個請求相同 并且 上一個請求已完成
- if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
- // 上一個請求已完成,那么重新啟動它
- if (!Preconditions.checkNotNull(previous).isRunning()) {
- previous.begin();
- }
- return target;
- }
- // 與上一個請求不同,則清除掉上一個,再將加入新請求
- requestManager.clear(target);
- target.setRequest(request);
- requestManager.track(target, request);
- return target;
- }
順著代碼次序,來看看這個方法每一步都干了什么:
- 首先執行buildRequest方法創建一個新的Request請求req1;
- 獲取當前ViewTarget上正在進行中的Request請求req2;
- 判斷新建的請求req1與已有的請求req2是否相同,如果相同則判斷是否跳過req2請求的緩存,兩個條件都滿足則開始執行begin()方法開始請求資源并停止往下執行,條件都不滿足則繼續執行第四步;
- 給ViewTarget設置最新的請求req1,然后執行track方法追蹤req1。
- 執行into(view)方法首先獲取到了Request請求,然后開始執行Request。如果是復用的Request則直接執行begin(),否則執行track(target, request),但最終仍然會執行begin();
- // ReqeustManager.java
- synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
- // 與lifecycle綁定
- targetTracker.track(target);
- // 啟動reqeust
- requestTracker.runRequest(request);
- }
- // RequestTracker.java
- public void runRequest(@NonNull Request request) {
- requests.add(request);
- if (!isPaused) {
- request.begin(); // 立即開始加載
- } else {
- //防止從以前的請求中加載任何位圖,釋放該請求所擁有的任何資源,顯示當前占位符(如果提供了該占位符),并將該請求標記為已取消。
- // request.java( Interface )
- request.clear();
- pendingRequests.add(request); // 加入隊列等待執行
- }
- }
- track方法的源碼,先是執行targetTracker.track(target)監聽ViewTarget的請求,然后runRequest開始執行。由于最終都是通過begin()方法開始請求,所以我們先來看看begin()方法的具體實現;
- Request類是interface類型,begin()它的抽象方法,所以我們要想弄清楚begin()的具體實現,那就要先找到Request的實現類,從buildRequest(xx)方法入手,同樣先貼出源碼:
- // RequestBuilder.java
- private Request buildRequest(
- Target<TranscodeType> target,
- @Nullable RequestListener<TranscodeType> targetListener,
- BaseRequestOptions<?> requestOptions,
- Executor callbackExecutor) {
- return buildRequestRecursive(
- /*requestLock=*/ new Object(),
- target,
- targetListener,
- /*parentCoordinator=*/ null,
- transitionOptions,
- requestOptions.getPriority(),
- requestOptions.getOverrideWidth(),
- requestOptions.getOverrideHeight(),
- requestOptions,
- callbackExecutor);
- }
- 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);
- // 請求成功了,直接返回縮略圖Request
- if (errorRequestCoordinator == null) {
- return mainRequest;
- }
- // ...
- Request errorRequest =
- errorBuilder.buildRequestRecursive(
- requestLock,
- target,
- targetListener,
- errorRequestCoordinator,
- errorBuilder.transitionOptions,
- errorBuilder.getPriority(),
- errorOverrideWidth,
- errorOverrideHeight,
- errorBuilder,
- callbackExecutor);
- // 同時返回縮略圖請求和錯誤請求
- errorRequestCoordinator.setRequests(mainRequest, errorRequest);
- return errorRequestCoordinator;
- }
顯然代碼里的mainRequest就是我們要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其內部我們發現Request最終其實是由SingleRequest.obtain方法產生,也就是說我們最終拿到的Request其實就是SingleReqeust類的一個實例。這里過程比較簡單,代碼就不貼出來了。我們直接去SingleReqeust類里面 看看begin方法如何實現的;
- // SingleReqeust.java
- public void begin() {
- if (status == Status.COMPLETE) {
- // 資源已下載,直接回調
- // 執行動畫
- onResourceReady(resource, DataSource.MEMORY_CACHE);
- return;
- }
- // 計算尺寸
- 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());
- }
- }
進入begin方法后首先判斷如果資源已經過加載好了則直接回調onResourceReady顯示圖片并緩存,否則測量出圖片尺寸后再開始加載圖片(onSizeReady()中執行加載任務)并同時顯示占位圖:
①overrideWith、overrideHeight通過override(width, height)設置:
Glide.with(mContext).load(url).override(75, 75).into(imageView);
②占位圖是用戶調用placeholder(resId)設置:
Glide.with(mContext).load(url).placeholder(resId).into(imageView);
接著再看onSizeReady()測量完圖片尺寸后如何加載圖片的:
- // SingleRequest.java
- @Override
- public void onSizeReady(int width, int height) {
- if (status != Status.WAITING_FOR_SIZE) {
- return;
- }
- status = Status.RUNNING;
- // 獲取圖片尺寸
- float sizeMultiplier = requestOptions.getSizeMultiplier();
- this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
- this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
- // 開始加載任務
- 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);
- }
- 可以看到真正的下載任務是在Engine類的load方法中實現的,其中也涉及到了圖片緩存邏輯;
- 最終通過Handler機制,Glide從工作線程切換到主線程,并最終將Drawable對象顯示到ImageView上;
總結
Glide初始化、顯示占位圖、圖片封面的整個業務流程都走完了;
可以從中學習glide中的設計模式:單例模式、工廠方法、策略模式等等,發現自己的不足之處;
Glide的圖片緩存可以看上一篇文章