宏觀剖析Glide4.8.0源碼

最近因為對項目的圖片庫做了功能拓展和優化,花了點時間研究了下Glide,輸出了總共6篇解析文章:
圖片框架 - Glide 4.11.0源碼走讀
圖片框架 - Glide自定義配置和組件及Registry機制
圖片框架 - Glide加載webp動圖流程解析
圖片框架 - Glide解碼webp動圖淺析
圖片框架 - Glide緩存機制解析
圖片框架 - Glide磁盤緩存研究

本篇文章對整個Glide源碼宏觀剖析做一個簡單總結。因為項目是基于Glide4.8.0,所以方便起見,分析的源碼也是4.8.0版本。為了閱讀方便,文章就盡量不貼對應代碼了,Glide代碼量確實有點多,想了解詳情的可以參考上面的6篇文章,里面有詳細源碼解析。

一、Glide整體結構

1.1整個Glide主干功能:
1.2 圖片加載整體執行流程:
Glide圖片加載流程

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

1.3 圖片加載中數據轉換流程
圖片加載數據轉換流程

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

二、Glide核心類圖

類關系

這里簡單例舉了部分核心類之間的關系:

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

三、相關執行流程

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

with主要干兩件事:

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

  • 初始化RequestManager;

3.1.2 load
load

load主要干一件事情:

  • 通過RequestManager初始化RequestBuilder。收集model和requestOption相關請求參數,為后續into封裝request做準備。
3.1.3 into
into

into主要干了三件事:

  • 封裝并發起request。
  • request獲取圖片數據。
  • 將圖片顯示到View上。
3.2 Glide整體緩存機制
3.2.1內存加載數據邏輯
內存獲取圖片資源邏輯

3.2.2 內存、磁盤、網絡請求整體存取邏輯

圖片數據存取邏輯

這里簡單總結下:

取邏輯:
內存 > 磁盤 > 網絡請求

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

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

  • 網絡請求:
    走網絡請求獲取圖片資源流。

存邏輯:

  • 內存:
    當前被加載的圖片資源存到activeResources中,下次加載資源切換時,當前activeResources會remove然后被轉移到lruCache。

  • 磁盤:
    網絡請求成功之后,獲取到資源流,然后看DiskCacheStrategy是否支持磁盤緩存,如果支持,會通過回調在SourceGenerator中通過cacheData進行資源緩存。

3.3 Glide磁盤緩存流程
網絡請求成功后onDataReady回調觸發的磁盤緩存流程

這里有兩個關鍵節點:原始數據流緩存和解碼后資源緩存。SourceGenerator本身除了發起網絡請求之外,也會在網絡請求成功后,在DiskCacheStrategy允許的條件下對原始數據進行磁盤緩存,其次在DecodeJob數據解碼成功后,在DiskCacheStrategy允許的條件下對解碼后的資源進行磁盤緩存。

這里有個問題:如果網絡請求成功后緩存的圖片原始數據流本身有問題,解碼失敗的話,框架頂多是不會緩存解碼后資源,但是對有問題的原始數據緩存不會做處理,這就會導致后續獲取圖片時按優先級優先獲取有問題的圖片原始數據緩存,導致問題。

解決分析:
客戶端層面:首先客戶端無法單獨判斷是否是解碼失敗,因為Glide網絡請求失敗、解碼失敗、IO失敗等等都統一拋的是onLoadFailed,其次對外暴露的api也沒有單獨清理一張圖片的,只有批量清理磁盤緩存,可以批量清,但這樣會影響到整體性能。也可以通過加signature繞過去,但是這樣每次都會去請求網絡,相當于不走緩存,最后也可以調整DiskCacheStrategy配置,不做原始數據緩存了,這個倒是可以。

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

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 {
    //解碼失敗,刪除之前緩存磁盤的原始數據文件
    diskCacheProvider.getDiskCache().delete(new DataCacheKey(currentSourceKey, signature));
    runGenerators();
  }
}

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

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

四、Glide中編譯時注解+APT的應用

最后再簡單介紹下Glide中編譯時注解+APT的應用。

注解的玩法主要有兩個場景:運行期和編譯期。

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

而Glide中,單例調用get()初始化時:

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動態生成的:

生成路徑

源碼對應的Processor路徑:

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

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

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

好了就寫這么多吧,Glide總體來說還是比較復雜的,本篇文章主要是對Glide做一個宏觀分析,以及工作中牽涉到的部分功能進行了淺析。在這個宏觀了解的基礎上,應該能夠對Glide框架局部問題的定位和分析提供一點幫助。當然文章中如有不對地方,歡迎批評指正!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370