宏觀剖析Glide4.8.0源碼

最近因?yàn)閷?xiàng)目的圖片庫做了功能拓展和優(yōu)化,花了點(diǎn)時(shí)間研究了下Glide,輸出了總共6篇解析文章:
圖片框架 - Glide 4.11.0源碼走讀
圖片框架 - Glide自定義配置和組件及Registry機(jī)制
圖片框架 - Glide加載webp動(dòng)圖流程解析
圖片框架 - Glide解碼webp動(dòng)圖淺析
圖片框架 - Glide緩存機(jī)制解析
圖片框架 - Glide磁盤緩存研究

本篇文章對整個(gè)Glide源碼宏觀剖析做一個(gè)簡單總結(jié)。因?yàn)轫?xiàng)目是基于Glide4.8.0,所以方便起見,分析的源碼也是4.8.0版本。為了閱讀方便,文章就盡量不貼對應(yīng)代碼了,Glide代碼量確實(shí)有點(diǎn)多,想了解詳情的可以參考上面的6篇文章,里面有詳細(xì)源碼解析。

一、Glide整體結(jié)構(gòu)

1.1整個(gè)Glide主干功能:
1.2 圖片加載整體執(zhí)行流程:
Glide圖片加載流程

Glide作為外部調(diào)用的入口函數(shù),主要收集請求參數(shù),構(gòu)建一個(gè)圖片請求,交由engine去獲取圖片資源,engine先從內(nèi)存獲取,如果活躍資源中有直接拿,如果沒有則嘗試去lruCache中獲取,如果也沒有則通過EngineJob線程池,發(fā)起一個(gè)異步任務(wù)即:DecodeJob來進(jìn)行磁盤和網(wǎng)絡(luò)獲取。這里磁盤緩存策略會(huì)根據(jù)DiskCacheStrategy來設(shè)置,它主要是配置對圖片原始數(shù)據(jù)流緩存以及解碼轉(zhuǎn)碼后資源緩存兩種類型數(shù)據(jù)。Generator是具體的圖片資源獲取管理類,它經(jīng)由ModelLoader-LoadData-具體Fetcher的層層內(nèi)部類調(diào)用關(guān)系最終交由對應(yīng)的Fetcher類去處理圖片資源獲取的任務(wù)。成功獲取資源后,會(huì)層層回調(diào)回來到DecodeJob來做解碼工作(如果是原始資源),解碼通過ModelLoader-LoadPath-具體Decoder的層層內(nèi)部類調(diào)用關(guān)系最終交由對應(yīng)的Decoder去做圖片資源解碼任務(wù)。后續(xù)就是將圖片設(shè)置到目標(biāo)控件上去。 這里,DecodeJob本身是通過Stage來調(diào)度自身不同任務(wù)類型,另外,網(wǎng)絡(luò)IO和本地IO是分屬于不同的DecodeJob,也就是兩者任務(wù)的轉(zhuǎn)換是需要通過EngineJob切線程來完成的。最后,Registry支持用戶自定義配置和組件。

1.3 圖片加載中數(shù)據(jù)轉(zhuǎn)換流程
圖片加載數(shù)據(jù)轉(zhuǎn)換流程

一目了然,就不過多解釋了。

二、Glide核心類圖

類關(guān)系

這里簡單例舉了部分核心類之間的關(guān)系:

  • Glide: 入口類。
  • RequestBuilder: 收集參數(shù),構(gòu)建Request和Target交由RequestManager去統(tǒng)一處理。
  • RequestManager:左膀右臂:TargetTracker負(fù)責(zé)Target對應(yīng)頁面生命周期綁定、RequestTracker負(fù)責(zé)發(fā)起Request請求。
  • Engine:加載引擎。
  • EngineJob:線程池。
  • DcodeJob:異步任務(wù),負(fù)責(zé)圖片獲取和解碼。
  • Generator:負(fù)責(zé)獲取圖片資源,這里分了三類(ResourceCache對應(yīng)解碼后緩存、DataCache對應(yīng)原始資源緩存、Source對應(yīng)網(wǎng)絡(luò)請求),通過ModelLoader最終匹配到對應(yīng)的Fetcher來執(zhí)行具體獲取圖片資源任務(wù)。
  • Target:顯示圖片的目標(biāo)控件。

三、相關(guān)執(zhí)行流程

3.1 首先看Glide.with(this).load(url).into(imageView) 整體調(diào)用流程
3.1.1 with
with

with主要干兩件事:

  • 圖片加載綁定對應(yīng)頁面生命周期;
    生命周期分為application 和 非application兩種。分別通過ApplicationLifecycle、ActivityFragmentLifecycle來管理生命周期。

  • 初始化RequestManager;

3.1.2 load
load

load主要干一件事情:

  • 通過RequestManager初始化RequestBuilder。收集model和requestOption相關(guān)請求參數(shù),為后續(xù)into封裝request做準(zhǔn)備。
3.1.3 into
into

into主要干了三件事:

  • 封裝并發(fā)起request。
  • request獲取圖片數(shù)據(jù)。
  • 將圖片顯示到View上。
3.2 Glide整體緩存機(jī)制
3.2.1內(nèi)存加載數(shù)據(jù)邏輯
內(nèi)存獲取圖片資源邏輯

3.2.2 內(nèi)存、磁盤、網(wǎng)絡(luò)請求整體存取邏輯

圖片數(shù)據(jù)存取邏輯

這里簡單總結(jié)下:

取邏輯:
內(nèi)存 > 磁盤 > 網(wǎng)絡(luò)請求

  • 內(nèi)存:
    上次剛被加載的資源(activeResources) > (最近被加載的資源)lruCache。

  • 磁盤:
    如果有主動(dòng)設(shè)置DiskCacheStrategy,則按設(shè)置來。如果配置的是DiskCacheStrategy.ALL:則是取轉(zhuǎn)換之后的資源(ResourceCache) > (DataCache)原始資源

  • 網(wǎng)絡(luò)請求:
    走網(wǎng)絡(luò)請求獲取圖片資源流。

存邏輯:

  • 內(nèi)存:
    當(dāng)前被加載的圖片資源存到activeResources中,下次加載資源切換時(shí),當(dāng)前activeResources會(huì)remove然后被轉(zhuǎn)移到lruCache。

  • 磁盤:
    網(wǎng)絡(luò)請求成功之后,獲取到資源流,然后看DiskCacheStrategy是否支持磁盤緩存,如果支持,會(huì)通過回調(diào)在SourceGenerator中通過cacheData進(jìn)行資源緩存。

3.3 Glide磁盤緩存流程
網(wǎng)絡(luò)請求成功后onDataReady回調(diào)觸發(fā)的磁盤緩存流程

這里有兩個(gè)關(guān)鍵節(jié)點(diǎn):原始數(shù)據(jù)流緩存和解碼后資源緩存。SourceGenerator本身除了發(fā)起網(wǎng)絡(luò)請求之外,也會(huì)在網(wǎng)絡(luò)請求成功后,在DiskCacheStrategy允許的條件下對原始數(shù)據(jù)進(jìn)行磁盤緩存,其次在DecodeJob數(shù)據(jù)解碼成功后,在DiskCacheStrategy允許的條件下對解碼后的資源進(jìn)行磁盤緩存。

這里有個(gè)問題:如果網(wǎng)絡(luò)請求成功后緩存的圖片原始數(shù)據(jù)流本身有問題,解碼失敗的話,框架頂多是不會(huì)緩存解碼后資源,但是對有問題的原始數(shù)據(jù)緩存不會(huì)做處理,這就會(huì)導(dǎo)致后續(xù)獲取圖片時(shí)按優(yōu)先級優(yōu)先獲取有問題的圖片原始數(shù)據(jù)緩存,導(dǎo)致問題。

解決分析:
客戶端層面:首先客戶端無法單獨(dú)判斷是否是解碼失敗,因?yàn)镚lide網(wǎng)絡(luò)請求失敗、解碼失敗、IO失敗等等都統(tǒng)一拋的是onLoadFailed,其次對外暴露的api也沒有單獨(dú)清理一張圖片的,只有批量清理磁盤緩存,可以批量清,但這樣會(huì)影響到整體性能。也可以通過加signature繞過去,但是這樣每次都會(huì)去請求網(wǎng)絡(luò),相當(dāng)于不走緩存,最后也可以調(diào)整DiskCacheStrategy配置,不做原始數(shù)據(jù)緩存了,這個(gè)倒是可以。

解決:我本身也不太喜歡直接gradle引三方庫,這樣一來不好拓展功能、二來不好定位修復(fù)問題。如果開源,我一般都會(huì)引入源碼,所以這里我直接改了源碼:

DecodeJob.java

private void decodeFromRetrievedData() {
  if (Log.isLoggable(TAG, Log.VERBOSE)) {
    logWithTimeAndKey("Retrieved data", startFetchTime,
       "data: " + currentData
           + ", cache key: " + currentSourceKey
           + ", fetcher: " + currentFetcher);
  }
  Resource<R> resource = null;
  try {
    resource = decodeFromData(currentFetcher, currentData, currentDataSource);
  } catch (GlideException e) {
    e.setLoggingDetails(currentAttemptingKey, currentDataSource);
   throwables.add(e);
  }
  if (resource != null) {
    notifyEncodeAndRelease(resource, currentDataSource);
  } else {
    //解碼失敗,刪除之前緩存磁盤的原始數(shù)據(jù)文件
    diskCacheProvider.getDiskCache().delete(new DataCacheKey(currentSourceKey, signature));
    runGenerators();
  }
}

具體分析過程參考文章:圖片框架 - Glide磁盤緩存研究

好了,經(jīng)過上面的圖文并茂的解析,應(yīng)該對Glide不管是整體還是部分都有了一個(gè)比較直觀的了解了。

四、Glide中編譯時(shí)注解+APT的應(yīng)用

最后再簡單介紹下Glide中編譯時(shí)注解+APT的應(yīng)用。

注解的玩法主要有兩個(gè)場景:運(yùn)行期和編譯期。

  • 運(yùn)行期:主要是注解+反射,注解提供標(biāo)簽,反射對注解類來做邏輯。
  • 編譯期:注解+APT+反射,這里APT(Annotation Processor Tool)注解處理器,編譯期會(huì)動(dòng)態(tài)生成類,而類的內(nèi)容要么自己手動(dòng)拼串組裝類內(nèi)容,要么使用JavaPoet封裝好的工具來組裝類內(nèi)容。

而Glide中,單例調(diào)用get()初始化時(shí):

private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
  GeneratedAppGlideModule result = null;
  try {
    Class<GeneratedAppGlideModule> clazz =
        (Class<GeneratedAppGlideModule>)
            Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
   result =
        clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
  } catch (ClassNotFoundException e) {
  ... 
 }
  return result;
}

這里的com.bumptech.glide.GeneratedAppGlideModuleImpl就是APT動(dòng)態(tài)生成的:

生成路徑

源碼對應(yīng)的Processor路徑:

annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'

這里就不詳細(xì)分析GeneratedAppGlideModuleImpl的生成了,它最終的功能是針對manifest 和 注解兩種注冊方式分別調(diào)用其applyOptions和registerComponents來觸發(fā)自定義配置和組件。

Demo地址:https://github.com/Zhto0/GlideWebpDemo

好了就寫這么多吧,Glide總體來說還是比較復(fù)雜的,本篇文章主要是對Glide做一個(gè)宏觀分析,以及工作中牽涉到的部分功能進(jìn)行了淺析。在這個(gè)宏觀了解的基礎(chǔ)上,應(yīng)該能夠?qū)lide框架局部問題的定位和分析提供一點(diǎn)幫助。當(dāng)然文章中如有不對地方,歡迎批評指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容