自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android源碼進(jìn)階之Glide加載流程和源碼詳解

移動(dòng)開(kāi)發(fā) Android
Glide是純Java寫的Android端開(kāi)源圖片加載庫(kù),能夠幫助我們下載、緩存、展示多種格式圖片,也包括GIF格式; 昨天我們從源碼里分析了,glide的緩存策略機(jī)制;那今天我們就趁熱打鐵來(lái)分析一波加載流程。

[[421043]]

本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。

前言

Glide是純Java寫的Android端開(kāi)源圖片加載庫(kù),能夠幫助我們下載、緩存、展示多種格式圖片,也包括GIF格式;

昨天我們從源碼里分析了,glide的緩存策略機(jī)制;

那今天我們就趁熱打鐵來(lái)分析一波加載流程;

一、glide常用的加載方法

1、加載圖片到imageView

  1. Glide.with(Context context).load(Strint url).into(ImageView imageView); 

2、各種形式的圖片加載到ImageView

  1. // 加載本地圖片 
  2. File file = new File(getExternalCacheDir() + "/image.jpg"); 
  3. Glide.with(this).load(file).into(imageView); 
  4. // 加載應(yīng)用資源 
  5. int resource = R.drawable.image; 
  6. Glide.with(this).load(resource).into(imageView); 
  7. // 加載二進(jìn)制流 
  8. byte[] image = getImageBytes(); 
  9. Glide.with(this).load(image).into(imageView); 
  10. // 加載Uri對(duì)象 
  11. Uri imageUri = getImageUri(); 
  12. Glide.with(this).load(imageUri).into(imageView); 

3、加載帶有占位圖

  1. Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView); 

占位圖目的為在目的圖片還未加載出來(lái)的時(shí)候,提前展示給用戶的一張圖片;

4、加載失敗 放置占位符

  1. Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error) 
  2.      .diskCacheStrategy(DiskCacheStrategy.NONE)//關(guān)閉Glide的硬盤緩存機(jī)制 
  3.      .into(imageView); 
  4. //DiskCacheStrategy.NONE:表示不緩存任何內(nèi)容。 
  5. //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。 
  6. //DiskCacheStrategy.RESULT:表示只緩存轉(zhuǎn)換過(guò)后的圖片(默認(rèn)選項(xiàng))。 
  7. //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉(zhuǎn)換過(guò)后的圖片。 

5、加載指定格式的圖片--指定為靜止圖片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .asBitmap()//只加載靜態(tài)圖片,如果是git圖片則只加載第一幀。 
  4.      .placeholder(R.drawable.loading) 
  5.      .error(R.drawable.error) 
  6.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  7.      .into(imageView); 

6、加載動(dòng)態(tài)圖片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .asGif()//加載動(dòng)態(tài)圖片,若現(xiàn)有圖片為非gif圖片,則直接加載錯(cuò)誤占位圖。 
  4.      .placeholder(R.drawable.loading) 
  5.      .error(R.drawable.error) 
  6.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  7.      .into(imageView); 

7、加載指定大小的圖片

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .placeholder(R.drawable.loading) 
  4.      .error(R.drawable.error) 
  5.      .diskCacheStrategy(DiskCacheStrategy.NONE) 
  6.      .override(100, 100)//指定圖片大小 
  7.      .into(imageView); 

8、關(guān)閉框架的內(nèi)存緩存機(jī)制

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .skipMemoryCache(true)  //傳入?yún)?shù)為false時(shí),則關(guān)閉內(nèi)存緩存。 
  4.      .into(imageView); 

9、關(guān)閉硬盤的緩存

  1. Glide.with(this) 
  2.      .load(url) 
  3.      .diskCacheStrategy(DiskCacheStrategy.NONE)     //關(guān)閉硬盤緩存操作 
  4.      .into(imageView); 
  5. //其他參數(shù)表示: 
  6. //DiskCacheStrategy.NONE:表示不緩存任何內(nèi)容。 
  7. //DiskCacheStrategy.SOURCE:表示只緩存原始圖片。 
  8. //DiskCacheStrategy.RESULT:表示只緩存轉(zhuǎn)換過(guò)后的圖片(默認(rèn)選項(xiàng))。 
  9. //DiskCacheStrategy.ALL :表示既緩存原始圖片,也緩存轉(zhuǎn)換過(guò)后的圖片。 

10、當(dāng)引用的 url 存在 token 時(shí)解決方法

  1. public class MyGlideUrl extends GlideUrl { 
  2.     private String mUrl; 
  3.     public MyGlideUrl(String url) { 
  4.         super(url); 
  5.         mUrl = url; 
  6.     } 
  7.     @Override 
  8.     public String getCacheKey() { 
  9.         return mUrl.replace(findTokenParam(), ""); 
  10.     } 
  11.     private String findTokenParam() { 
  12.         String tokenParam = ""
  13.         int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token="); 
  14.         if (tokenKeyIndex != -1) { 
  15.             int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1); 
  16.             if (nextAndIndex != -1) { 
  17.                 tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1); 
  18.             } else { 
  19.                 tokenParam = mUrl.substring(tokenKeyIndex); 
  20.             } 
  21.         } 
  22.         return tokenParam; 
  23.     } 

然后加載圖片的方式為:

  1. Glide.with(this) 
  2.      .load(new MyGlideUrl(url)) 
  3.      .into(imageView); 

11、利用Glide將圖片加載到不同控件或加載成不同使用方式

(1)、拿到圖片實(shí)例

  1. //1、通過(guò)自己構(gòu)造 target 可以獲取到圖片實(shí)例 
  2. SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() { 
  3.     @Override 
  4.     public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
  5.         imageView.setImageDrawable(resource); 
  6.     } 
  7. }; 
  8. //2、將圖片實(shí)例記載到指定的imageview上,也可以做其他的事情 
  9. public void loadImage(View view) { 
  10.     String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
  11.     Glide.with(this) 
  12.          .load(url) 
  13.          .into(simpleTarget); 

(2)、將圖片加載到任何位置

  1. /* 
  2. *將圖片加載為控件背景 
  3. */ 
  4. public class MyLayout extends LinearLayout { 
  5.     private ViewTarget<MyLayout, GlideDrawable> viewTarget; 
  6.     public MyLayout(Context context, AttributeSet attrs) { 
  7.         super(context, attrs); 
  8.         viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) { 
  9.             @Override 
  10.             public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
  11.                 MyLayout myLayout = getView(); 
  12.                 myLayout.setImageAsBackground(resource); 
  13.             } 
  14.         }; 
  15.     } 
  16.     public ViewTarget<MyLayout, GlideDrawable> getTarget() { 
  17.         return viewTarget; 
  18.     } 
  19.     public void setImageAsBackground(GlideDrawable resource) { 
  20.         setBackground(resource); 
  21.     } 
  22. //引用圖片到指定控件作為背景 
  23. public class MainActivity extends AppCompatActivity { 
  24.     MyLayout myLayout; 
  25.     @Override 
  26.     protected void onCreate(Bundle savedInstanceState) { 
  27.         super.onCreate(savedInstanceState); 
  28.         setContentView(R.layout.activity_main); 
  29.         myLayout = (MyLayout) findViewById(R.id.background); 
  30.     } 
  31.     public void loadImage(View view) { 
  32.         String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
  33.         Glide.with(this) 
  34.              .load(url) 
  35.              .into(myLayout.getTarget()); 
  36.     } 

12、Glide 實(shí)現(xiàn)預(yù)加載

  1. //a、預(yù)加載代碼 
  2. Glide.with(this) 
  3.      .load(url) 
  4.      .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
  5.      .preload(); 
  6. //preload() 有兩種重載 
  7.  // 1、帶有參數(shù)的重載,參數(shù)作用是設(shè)置預(yù)加載的圖片大?。?nbsp;
  8. //2、不帶參數(shù)的表示加載的圖片為原始尺寸; 
  9. //b、使用預(yù)加載的圖片 
  10. Glide.with(this) 
  11.      .load(url) 
  12.      .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
  13.      .into(imageView); 

二、Glide加載流程詳解

1、with(context)

  1. // Glide.java 
  2. public static RequestManager with(@NonNull Context context) { 
  3.     return getRetriever(context).get(context); 
  4. public static RequestManager with(@NonNull Activity activity) { 
  5.     return getRetriever(activity).get(activity); 
  6. public static RequestManager with(@NonNull FragmentActivity activity) { 
  7.     return getRetriever(activity).get(activity); 
  8. public static RequestManager with(@NonNull Fragment fragment) { 
  9.     return getRetriever(fragment.getContext()).get(fragment); 
  10. public static RequestManager with(@NonNull View view) { 
  11.     return getRetriever(view.getContext()).get(view); 

該函數(shù)創(chuàng)建了Glide實(shí)例并初始化了一些基本參數(shù),然后創(chuàng)建了一個(gè)RequestManager對(duì)象并返回??偣灿?個(gè)場(chǎng)景,這里就先選取參數(shù)為Context類型情形進(jìn)行分析。

  1. // Glide.java 
  2. public static RequestManager with(@NonNull Context context) { 
  3.     return getRetriever(context).get(context); 

可以看到該函數(shù)首先調(diào)用了getRetriever(context)獲取到了RequestManagerRetriever對(duì)象。在創(chuàng)建該對(duì)象之前首先通過(guò)Glide.java中的get方法獲得了Glide實(shí)例(Glide是一個(gè)單例),同時(shí)讀取AppGlideModule和AndroidManifest.xml的配置。

  1. // Glide.java 
  2. private static RequestManagerRetriever getRetriever(@Nullable Context context) { 
  3.     // Glide.get(context)獲取Glide實(shí)例 
  4.     return Glide.get(context).getRequestManagerRetriever(); 
  5. public static Glide get(@NonNull Context context) { 
  6.     if (glide == null) { 
  7.       // 加載AppGlideModule 
  8.       GeneratedAppGlideModule annotationGeneratedModule = 
  9.           getAnnotationGeneratedGlideModules(context.getApplicationContext()); 
  10.       synchronized (Glide.class) { 
  11.         if (glide == null) { 
  12.           // 加載Mainfest配置、注冊(cè)模塊回調(diào) 
  13.           // 這一步執(zhí)行了 Glide.build()方法構(gòu)造Glide實(shí)例。build方法下面會(huì)講到 
  14.           checkAndInitializeGlide(context, annotationGeneratedModule); 
  15.         } 
  16.       } 
  17.     } 
  18.     return glide; 
  19.   } 

獲取到Glide實(shí)例后,緊接著調(diào)用getRequestManagerRetriever方法返回了上一步已經(jīng)初始化好的RequestManagerRetriever對(duì)象。

  1. // Glide.java 
  2.   public RequestManagerRetriever getRequestManagerRetriever() { 
  3.     return requestManagerRetriever; 
  4.   } 

接著再看一看RequestManagerRetriever是如何被初始化的,以及初始化過(guò)程中都干了哪些事。首先貼源碼看看Glide.build方法內(nèi)部具體實(shí)現(xiàn)(該方法在上述checkAndInitializeGlide()函數(shù)中被調(diào)用):

  1. // GlideBuilder.java 
  2. Glide build(@NonNull Context context) { 
  3.       // 分配線程池、配置緩存策略 
  4.       sourceExecutor = GlideExecutor.newSourceExecutor(); 
  5.       diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); 
  6.       animationExecutor = GlideExecutor.newAnimationExecutor(); 
  7.       memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); 
  8.       // 監(jiān)聽(tīng)網(wǎng)絡(luò)變化 
  9.       connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); 
  10.       int size = memorySizeCalculator.getBitmapPoolSize(); 
  11.       if (size > 0) { 
  12.         bitmapPool = new LruBitmapPool(size); 
  13.       } else { 
  14.         bitmapPool = new BitmapPoolAdapter(); 
  15.       } 
  16.       arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); 
  17.       memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 
  18.       diskCacheFactory = new InternalCacheDiskCacheFactory(context); 
  19.     // engine是負(fù)責(zé)執(zhí)行加載任務(wù)的 
  20.     if (engine == null) { 
  21.       engine = 
  22.           new Engine( 
  23.               memoryCache, 
  24.               diskCacheFactory, 
  25.               diskCacheExecutor, 
  26.               sourceExecutor, 
  27.               GlideExecutor.newUnlimitedSourceExecutor(), 
  28.               animationExecutor, 
  29.               isActiveResourceRetentionAllowed); 
  30.     } 
  31.     if (defaultRequestListeners == null) { 
  32.       defaultRequestListeners = Collections.emptyList(); 
  33.     } else { 
  34.       defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); 
  35.     } 
  36.     RequestManagerRetriever requestManagerRetriever = 
  37.         new RequestManagerRetriever(requestManagerFactory); 
  38.     return new Glide( 
  39.         context, 
  40.         engine, 
  41.         memoryCache, 
  42.         bitmapPool, 
  43.         arrayPool, 
  44.         requestManagerRetriever, 
  45.         connectivityMonitorFactory, 
  46.         logLevel, 
  47.         defaultRequestOptionsFactory, 
  48.         defaultTransitionOptions, 
  49.         defaultRequestListeners, 
  50.         isLoggingRequestOriginsEnabled, 
  51.         isImageDecoderEnabledForBitmaps); 
  52.   } 
  • 執(zhí)行Glide.get()方法時(shí)就已經(jīng)分配好了資源加載、緩存線程池、配置好了緩存策略,這里的engine專門負(fù)責(zé)加載、解碼資源,ConnectivityMonitor注冊(cè)了網(wǎng)絡(luò)狀態(tài)監(jiān)聽(tīng)器,當(dāng)網(wǎng)絡(luò)斷開(kāi)時(shí)暫停請(qǐng)求網(wǎng)絡(luò)資源,重連后繼續(xù)請(qǐng)求資源;
  • RequestManagerRetriever是原來(lái)是通過(guò)RequestManagerFactory工廠類構(gòu)造的。進(jìn)入到RequestManagerFactory.java類中,可以看到get方法獲取到了相應(yīng)的RequestManager對(duì)象;
  • 從這里我們可以發(fā)現(xiàn),無(wú)論哪種情況,當(dāng)App進(jìn)入后臺(tái)后會(huì)導(dǎo)致頁(yè)面不可見(jiàn),此時(shí)RequestManager綁定到了ApplicationContext,與App的生命周期一致,因此在RequestManager.java類中也實(shí)現(xiàn)了生命周期相關(guān)的回調(diào)函數(shù);
  1. // RequestManagerRetriever.java 
  2. // get有好幾個(gè)重載方法,這里僅選取context參數(shù)進(jìn)行分析 
  3. public RequestManager get(@NonNull Context context) { 
  4.     if (Util.isOnMainThread() && !(context instanceof Application)) { 
  5.       if (context instanceof FragmentActivity) { 
  6.         return get((FragmentActivity) context); 
  7.       } else if (context instanceof Activity) { 
  8.         return get((Activity) context); 
  9.       } else if (context instanceof ContextWrapper 
  10.           && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { 
  11.         return get(((ContextWrapper) context).getBaseContext()); 
  12.       } 
  13.     } 
  14.     return getApplicationManager(context); 
  15.   } 

執(zhí)行完Glide.with(context)后我們拿到了一個(gè)對(duì)應(yīng)的RequestManager對(duì)象,接下來(lái)就執(zhí)行下一個(gè)任務(wù)load(url);

2、load(url)

拿到了RequestManager,緊接著調(diào)用load方法開(kāi)始執(zhí)行下一步操作,同樣先看看load方法的實(shí)現(xiàn)

  1. // RequestManager.java 
  2. public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) { 
  3.     return asDrawable().load(bitmap); 
  4.   } 
  5.   public RequestBuilder<Drawable> load(@Nullable Drawable drawable) { 
  6.     return asDrawable().load(drawable); 
  7.   } 
  8.   public RequestBuilder<Drawable> load(@Nullable String string) { 
  9.     return asDrawable().load(string); 
  10.   } 
  11.   public RequestBuilder<Drawable> load(@Nullable Uri uri) { 
  12.     return asDrawable().load(uri); 
  13.   } 
  14.   public RequestBuilder<Drawable> load(@Nullable File file) { 
  15.     return asDrawable().load(file); 
  16.   } 
  17.   public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) { 
  18.     return asDrawable().load(resourceId); 
  19.   } 
  20.   public RequestBuilder<Drawable> load(@Nullable URL url) { 
  21.     return asDrawable().load(url); 
  22.   } 
  23.   public RequestBuilder<Drawable> load(@Nullable byte[] model) { 
  24.     return asDrawable().load(model); 
  25.   } 
  26.   public RequestBuilder<Drawable> load(@Nullable Object model) { 
  27.     return asDrawable().load(model); 
  28.   } 
  • load()同樣有多個(gè)重載函數(shù),傳入的參數(shù)可以是圖片對(duì)象Bitmap、Drawable、本地資源Uri、在線資源路徑Url、文件對(duì)象File、assets資源的id,這里我們只看參數(shù)為Url的情形;
  • asDrawable().load(url)返回了一個(gè)RequestBuilder對(duì)象,首先看看asDrawable方法干了什么;
  1. // RequestManager.java 
  2.   public RequestBuilder<Drawable> asDrawable() { 
  3.     return as(Drawable.class); 
  4.   } 
  5.   public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) { 
  6.     return new RequestBuilder<>(glide, this, resourceClass, context); 
  7.   } 

asDrawable方法創(chuàng)建了RequestBuilder對(duì)象,然后調(diào)用RequestBuilder.java中的load方法;

  1. // RequestBuilder.java 
  2.   // 傳入的String類型的url將會(huì)被作為緩存的key 
  3.   public RequestBuilder<TranscodeType> load(@Nullable String string) { 
  4.     return loadGeneric(string); 
  5.   } 
  6.   // 這里返回了自身 
  7.   private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { 
  8.     this.model = model; 
  9.     isModelSet = true
  10.     return this; 
  11.   } 

load函數(shù)主要工作就是根據(jù)傳入的資源類型,構(gòu)造了一個(gè)相應(yīng)的RequestBuilder對(duì)象。至此一切準(zhǔn)備工作準(zhǔn)備就緒,接下來(lái)就是最為重要的一步了-加載、展示文件,讓我們來(lái)著看into(view)方法如何完成這些任務(wù);

3、into(view)

拿到的是對(duì)應(yīng)類型RequestBuilder實(shí)例,那么就看看該類里into方法的具體實(shí)現(xiàn)。同樣into方法有into(@NonNull Y target)和into(@NonNull ImageView )兩個(gè)重載函數(shù)(這兩個(gè)函數(shù)最終都會(huì)走到同一個(gè)函數(shù)中),由于調(diào)用into方法時(shí)我們傳入的參數(shù)是ImageView類型的;

  1. // RequestBuilder.java 
  2.   public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { 
  3.     Util.assertMainThread(); 
  4.     BaseRequestOptions<?> requestOptions = this; 
  5.     // View's scale type. 
  6.     // 處理圖片縮放,根據(jù)縮放類型來(lái)初始化對(duì)應(yīng)的requestOptions對(duì)象 
  7.     ...... 
  8.     return into
  9.         glideContext.buildImageViewTarget(view, transcodeClass), 
  10.         /*targetListener=*/ null
  11.         requestOptions, 
  12.         Executors.mainThreadExecutor() // 運(yùn)行在主線程的handler 
  13.     ); 
  14.   } 
  • 上面代碼段首先處理圖片縮放類型(裁剪、對(duì)齊方式等),并將生成的相關(guān)參數(shù)放入了requestOptions對(duì)象中,然后再將其作為參數(shù)傳給了RequestBuilder.java類私有方法into。該方法定義的四個(gè)參數(shù)分別為:viewTarget、target回調(diào)監(jiān)聽(tīng)器、請(qǐng)求參數(shù)、主線程的回調(diào)函數(shù);
  • 顯然外部傳入ImageView對(duì)象最終被轉(zhuǎn)換成了ViewTarget對(duì)象,轉(zhuǎn)換函數(shù)便是glideContext.buildImageViewTarget(view, transcodeClass);
  1. // GlideContext.java 
  2.   public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) { 
  3.     return imageViewTargetFactory.buildTarget(imageView, transcodeClass); 
  4.   } 

ViewTarget又是由ImageViewTargetFactory工廠方法生成,接著再看buildTarget方法是如何生成ViewTarget對(duì)象。

  1. // imageViewTargetFactory.java 
  2. public class ImageViewTargetFactory { 
  3.   public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) { 
  4.     if (Bitmap.class.equals(clazz)) { 
  5.       return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); 
  6.     } else if (Drawable.class.isAssignableFrom(clazz)) { 
  7.       return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view); 
  8.     } else { 
  9.       throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as(Class).transcode(ResourceTranscoder)"); 
  10.     } 
  11.   } 

可以看到無(wú)論傳入?yún)?shù)是何種類型,最終都會(huì)轉(zhuǎn)換成兩種類型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;這里如何選擇取決于asBitmap()、asGif()和asDrawable()函數(shù)是否被調(diào)用,默認(rèn)是Bitmap類型,所以這里默認(rèn)返回的是BitmapImageViewTarget;

  1. // BitmapImageViewTarget.java 
  2. public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> { 
  3.   public BitmapImageViewTarget(ImageView view) { 
  4.     super(view); 
  5.   } 
  6.   @Override 
  7.   protected void setResource(Bitmap resource) { 
  8.     view.setImageBitmap(resource); // 顯示圖片 
  9.   } 

至此ViewTarget創(chuàng)建完畢,我們?cè)倩氐絉equestBuilder.java私有into方法

  1. // RequestBuilder.java` 
  2.  private <Y extends Target<TranscodeType>> Y into
  3.       @NonNull Y target, 
  4.       @Nullable RequestListener<TranscodeType> targetListener, 
  5.       BaseRequestOptions<?> options, 
  6.       Executor callbackExecutor) { 
  7.     // 注釋1:創(chuàng)建request 
  8.     Request request = buildRequest(target, targetListener, options, callbackExecutor); 
  9.     // 獲取前一個(gè)reqeust請(qǐng)求對(duì)象 
  10.     Request previous = target.getRequest(); 
  11.     // 與上一個(gè)請(qǐng)求相同 并且 上一個(gè)請(qǐng)求已完成 
  12.     if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
  13.      // 上一個(gè)請(qǐng)求已完成,那么重新啟動(dòng)它 
  14.       if (!Preconditions.checkNotNull(previous).isRunning()) { 
  15.         previous.begin(); 
  16.       } 
  17.       return target; 
  18.     } 
  19.     // 與上一個(gè)請(qǐng)求不同,則清除掉上一個(gè),再將加入新請(qǐng)求 
  20.     requestManager.clear(target); 
  21.     target.setRequest(request); 
  22.     requestManager.track(target, request); 
  23.     return target; 
  24.   } 

順著代碼次序,來(lái)看看這個(gè)方法每一步都干了什么:

  • 首先執(zhí)行buildRequest方法創(chuàng)建一個(gè)新的Request請(qǐng)求req1;
  • 獲取當(dāng)前ViewTarget上正在進(jìn)行中的Request請(qǐng)求req2;
  • 判斷新建的請(qǐng)求req1與已有的請(qǐng)求req2是否相同,如果相同則判斷是否跳過(guò)req2請(qǐng)求的緩存,兩個(gè)條件都滿足則開(kāi)始執(zhí)行begin()方法開(kāi)始請(qǐng)求資源并停止往下執(zhí)行,條件都不滿足則繼續(xù)執(zhí)行第四步;
  • 給ViewTarget設(shè)置最新的請(qǐng)求req1,然后執(zhí)行track方法追蹤req1。
  • 執(zhí)行into(view)方法首先獲取到了Request請(qǐng)求,然后開(kāi)始執(zhí)行Request。如果是復(fù)用的Request則直接執(zhí)行begin(),否則執(zhí)行track(target, request),但最終仍然會(huì)執(zhí)行begin();
  1. // ReqeustManager.java   
  2. synchronized void track(@NonNull Target<?> target, @NonNull Request request) { 
  3.     // 與lifecycle綁定 
  4.     targetTracker.track(target); 
  5.     // 啟動(dòng)reqeust 
  6.     requestTracker.runRequest(request); 
  7.   } 
  8. // RequestTracker.java 
  9.   public void runRequest(@NonNull Request request) { 
  10.     requests.add(request); 
  11.     if (!isPaused) { 
  12.       request.begin(); // 立即開(kāi)始加載 
  13.     } else { 
  14.       //防止從以前的請(qǐng)求中加載任何位圖,釋放該請(qǐng)求所擁有的任何資源,顯示當(dāng)前占位符(如果提供了該占位符),并將該請(qǐng)求標(biāo)記為已取消。 
  15.       // request.java( Interface ) 
  16.       request.clear(); 
  17.       pendingRequests.add(request); // 加入隊(duì)列等待執(zhí)行 
  18.     } 
  19.   } 
  • track方法的源碼,先是執(zhí)行targetTracker.track(target)監(jiān)聽(tīng)ViewTarget的請(qǐng)求,然后runRequest開(kāi)始執(zhí)行。由于最終都是通過(guò)begin()方法開(kāi)始請(qǐng)求,所以我們先來(lái)看看begin()方法的具體實(shí)現(xiàn);
  • Request類是interface類型,begin()它的抽象方法,所以我們要想弄清楚begin()的具體實(shí)現(xiàn),那就要先找到Request的實(shí)現(xiàn)類,從buildRequest(xx)方法入手,同樣先貼出源碼:
  1. // RequestBuilder.java 
  2. private Request buildRequest( 
  3.       Target<TranscodeType> target, 
  4.       @Nullable RequestListener<TranscodeType> targetListener, 
  5.       BaseRequestOptions<?> requestOptions, 
  6.       Executor callbackExecutor) { 
  7.     return buildRequestRecursive( 
  8.         /*requestLock=*/ new Object(), 
  9.         target, 
  10.         targetListener, 
  11.         /*parentCoordinator=*/ null
  12.         transitionOptions, 
  13.         requestOptions.getPriority(), 
  14.         requestOptions.getOverrideWidth(), 
  15.         requestOptions.getOverrideHeight(), 
  16.         requestOptions, 
  17.         callbackExecutor); 
  18.   } 
  19. private Request buildRequestRecursive( 
  20.       Object requestLock, 
  21.       Target<TranscodeType> target, 
  22.       @Nullable RequestListener<TranscodeType> targetListener, 
  23.       @Nullable RequestCoordinator parentCoordinator, 
  24.       TransitionOptions<?, ? super TranscodeType> transitionOptions, 
  25.       Priority priority, 
  26.       int overrideWidth, 
  27.       int overrideHeight, 
  28.       BaseRequestOptions<?> requestOptions, 
  29.       Executor callbackExecutor) { 
  30.     // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator. 
  31.     ErrorRequestCoordinator errorRequestCoordinator = null
  32.     // 請(qǐng)求出錯(cuò)了 
  33.     if (errorBuilder != null) { 
  34.       errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator); 
  35.       parentCoordinator = errorRequestCoordinator; 
  36.     } 
  37.     // 無(wú)法確認(rèn)完成請(qǐng)求和縮略圖請(qǐng)求哪個(gè)先完成,所以當(dāng)縮略圖比完成請(qǐng)求后完成時(shí)就不再顯示縮略圖 
  38.     Request mainRequest = 
  39.         buildThumbnailRequestRecursive( 
  40.             requestLock, 
  41.             target, 
  42.             targetListener, 
  43.             parentCoordinator, 
  44.             transitionOptions, 
  45.             priority, 
  46.             overrideWidth, 
  47.             overrideHeight, 
  48.             requestOptions, 
  49.             callbackExecutor); 
  50.     // 請(qǐng)求成功了,直接返回縮略圖Request 
  51.     if (errorRequestCoordinator == null) { 
  52.       return mainRequest; 
  53.     } 
  54.     // ... 
  55.     Request errorRequest = 
  56.         errorBuilder.buildRequestRecursive( 
  57.             requestLock, 
  58.             target, 
  59.             targetListener, 
  60.             errorRequestCoordinator, 
  61.             errorBuilder.transitionOptions, 
  62.             errorBuilder.getPriority(), 
  63.             errorOverrideWidth, 
  64.             errorOverrideHeight, 
  65.             errorBuilder, 
  66.             callbackExecutor); 
  67.     // 同時(shí)返回縮略圖請(qǐng)求和錯(cuò)誤請(qǐng)求 
  68.     errorRequestCoordinator.setRequests(mainRequest, errorRequest); 
  69.     return errorRequestCoordinator; 
  70.   } 

顯然代碼里的mainRequest就是我們要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其內(nèi)部我們發(fā)現(xiàn)Request最終其實(shí)是由SingleRequest.obtain方法產(chǎn)生,也就是說(shuō)我們最終拿到的Request其實(shí)就是SingleReqeust類的一個(gè)實(shí)例。這里過(guò)程比較簡(jiǎn)單,代碼就不貼出來(lái)了。我們直接去SingleReqeust類里面 看看begin方法如何實(shí)現(xiàn)的;

  1. // SingleReqeust.java 
  2. public void begin() { 
  3.       if (status == Status.COMPLETE) { 
  4.         // 資源已下載,直接回調(diào) 
  5.         // 執(zhí)行動(dòng)畫 
  6.         onResourceReady(resource, DataSource.MEMORY_CACHE); 
  7.         return
  8.       } 
  9.         // 計(jì)算尺寸 
  10.       if (Util.isValidDimensions(overrideWidth, overrideHeight)) { 
  11.         onSizeReady(overrideWidth, overrideHeight); 
  12.       } else { 
  13.         target.getSize(this); 
  14.       } 
  15.       if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) 
  16.           && canNotifyStatusChanged()) { 
  17.         // 開(kāi)始加載 
  18.         // 設(shè)置占位度 
  19.         target.onLoadStarted(getPlaceholderDrawable()); 
  20.       } 
  21.   } 

進(jìn)入begin方法后首先判斷如果資源已經(jīng)過(guò)加載好了則直接回調(diào)onResourceReady顯示圖片并緩存,否則測(cè)量出圖片尺寸后再開(kāi)始加載圖片(onSizeReady()中執(zhí)行加載任務(wù))并同時(shí)顯示占位圖:

①overrideWith、overrideHeight通過(guò)override(width, height)設(shè)置:

Glide.with(mContext).load(url).override(75, 75).into(imageView);

②占位圖是用戶調(diào)用placeholder(resId)設(shè)置:

Glide.with(mContext).load(url).placeholder(resId).into(imageView);

接著再看onSizeReady()測(cè)量完圖片尺寸后如何加載圖片的:

  1. // SingleRequest.java 
  2. @Override 
  3.   public void onSizeReady(int width, int height) { 
  4.       if (status != Status.WAITING_FOR_SIZE) { 
  5.         return
  6.       } 
  7.       status = Status.RUNNING; 
  8.       // 獲取圖片尺寸 
  9.       float sizeMultiplier = requestOptions.getSizeMultiplier(); 
  10.       this.width = maybeApplySizeMultiplier(width, sizeMultiplier); 
  11.       this.height = maybeApplySizeMultiplier(height, sizeMultiplier); 
  12.       // 開(kāi)始加載任務(wù) 
  13.       loadStatus = 
  14.           engine.load
  15.               glideContext, 
  16.               model, 
  17.               requestOptions.getSignature(), 
  18.               this.width, 
  19.               this.height, 
  20.               requestOptions.getResourceClass(), 
  21.               transcodeClass, 
  22.               priority, 
  23.               requestOptions.getDiskCacheStrategy(), 
  24.               requestOptions.getTransformations(), 
  25.               requestOptions.isTransformationRequired(), 
  26.               requestOptions.isScaleOnlyOrNoTransform(), 
  27.               requestOptions.getOptions(), 
  28.               requestOptions.isMemoryCacheable(), 
  29.               requestOptions.getUseUnlimitedSourceGeneratorsPool(), 
  30.               requestOptions.getUseAnimationPool(), 
  31.               requestOptions.getOnlyRetrieveFromCache(), 
  32.               this, 
  33.               callbackExecutor); 
  34.   } 
  • 可以看到真正的下載任務(wù)是在Engine類的load方法中實(shí)現(xiàn)的,其中也涉及到了圖片緩存邏輯;
  • 最終通過(guò)Handler機(jī)制,Glide從工作線程切換到主線程,并最終將Drawable對(duì)象顯示到ImageView上;

總結(jié)

Glide初始化、顯示占位圖、圖片封面的整個(gè)業(yè)務(wù)流程都走完了;

可以從中學(xué)習(xí)glide中的設(shè)計(jì)模式:?jiǎn)卫J?、工廠方法、策略模式等等,發(fā)現(xiàn)自己的不足之處;

 

Glide的圖片緩存可以看上一篇文章

 

責(zé)任編輯:武曉燕 來(lái)源: Android開(kāi)發(fā)編程
相關(guān)推薦

2021-09-01 06:48:16

AndroidGlide緩存

2021-08-10 20:41:33

AndroidApp流程

2021-09-03 07:27:38

AndroidGlide管理

2021-09-07 06:40:25

AndroidLiveData原理

2021-08-17 13:41:11

AndroidView事件

2021-08-05 20:39:34

AndroidKotlinStandard.kt

2021-09-12 07:30:10

配置

2021-09-09 06:55:43

AndroidViewDragHel原理

2021-09-30 07:36:51

AndroidViewDraw

2015-03-31 18:26:43

陌陌社交

2021-08-12 16:28:10

AndroidHandleLooper

2021-09-08 06:51:52

AndroidRetrofit原理

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-08-23 06:27:46

AndroidctivitysetContentV

2021-09-05 07:35:58

lifecycleAndroid組件原理

2021-09-13 15:17:52

FastThreadL源碼Java

2009-11-30 16:38:30

Android

2021-08-24 07:53:28

AndroidActivity生命周期

2011-06-23 13:10:39

Python 對(duì)象機(jī)制

2015-03-23 17:52:05

Android倉(cāng)庫(kù)管理系統(tǒng)SQLight
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)