Glide架構設計藝術

自從Android誕生以來,Bitmap的管理就一直是大問題,爲了更好的管理它,不同的圖片加載框架不斷的被推出,從剛開始的ImageLoader,到Picasso,再到現在的Fresco和Glide,可謂百花齊放。然而前兩者現在都已經不再維護了,同時我們公司的項目目前也已經從Fresco切換到Glide了,之前之所以用Fresco是因爲他在Android5.0以下系統中能從native層“偷”內存,但後面由於Android對於Bitmap內存管理方式的改變,這個功能不再生效,相比於Glide來說,Fresco就顯得侵入性太強,而且可擴展性沒有glide強。而Glide之所以擴展性如此強,就在於它 優秀的架構設計 ,這也是我們今天要討論的。

1. 總體架構

首先我們看下Glide總體架構圖:



從架構圖上,我們可以看出,glide並沒有着眼於bitmap,而是進行了高度的抽象,所以我們不應該將glide看成一個圖片加載框架,而是一個資源從不同形態之間轉換的框架,從url到bitmap只是其中一種,所以我們不難理解glide甚至能從視頻加載第一幀,因爲它沒有對輸入類型做任何限制,都是統一抽象成的Request。

2. 從Request到Data的設計——資源加載模塊設計

首先我們看下總體過程:



這裏以從url到inputstream二進制流爲例,之所以這裏有三條分路,這和glide的緩存策略有關。首先有一點要澄清,這裏從Request到Data其實是跳過了內存緩存的介紹,畢竟如果內存中已有bitmap緩存,我們直接取用就可以了,無需這麼麻煩(詳細的緩存方案後續文章會介紹)。因此,這裏有三條路徑主要是磁盤緩存和網絡緩存,而磁盤緩存有兩種:

  • DataCache 從原始Request加載到的二進制流直接緩存,比如從url加載的原圖緩存
  • ResourceCache 將從Request獲取的Data數據處理後緩存,比如將一個url的原圖進行壓縮後又緩存起來,glide能夠緩存不同尺寸的圖片的原因就在於這一步。
    而SourceGenerator就是跳過緩存直接從原始Request獲取請求了。

2.1 Request是如何被加載的

由於glide的這種高度抽象,現在我們面臨着一個問題:如此多類型的Request和如此多的Data,具體怎麼去加載它呢?比如說Request有url、uri、File、資源id、視頻等等,不同的Request肯定有不同的加載方式,同一個Request既可能從網絡加載,也可能從磁盤加載,可能性太多,那麼我們怎麼去加載呢?if-else去判斷嗎? 一個優秀的框架肯定不會幹這種low到爆的事。這裏我們介紹一些新的角色:


這裏,針對每一種Request,我們都有對應的ModelLoader,當一個Request進來時,我們可以遍歷所有的ModelLoader,通過handles()方法判斷這個ModelLoader能否處理這種Request,這樣我們就能解決第一個問題,即不同的Request如何管理加載,有了ModelLoader機制,如果我們想增加一種Request,我們只要開發對應的ModelLoader即可。
有了ModelLoader,其實是不夠的,它只是用來判斷這個Request是否能否處理,爲了能真正的加載請求,Glide引入了DataFetcher,不同的方式對應一個不同的DataFetcher,兩者職責分離,這是因爲同一種Request其實有很多加載方式,比如從網絡加載,從磁盤加載等等,非常複雜,所以這裏獨立出一個DataFetcher。其中LoadData只是對DataFetcher的一種包裝,多包含了一些信息而已。

2.2 小結

現在,我們根據傳入的請求具體類型(比如url還是file還是字節數組),通過遍歷所有的ModelLoader判斷該ModelLoader能否處理這種請求,然後用該ModelLoader中的DataFetcher去具體加載這個請求。

3. 從Data到Resource的設計——解碼和轉碼模塊設計

有了ModelLoader和DataFetcher機制,Glide已經能方便的將一個原始請求從不同的地方加載到內存中了,這個時候這份數據還只是單純的二進制數據(攜帶了格式數據)而已,我們稱其爲Data,現在需要進行解碼過程,剔除原始的格式信息,然後拿原始信息重新編碼,將其轉化成不同的格式,比如將一個jpg先解碼然後轉碼成Bitmap,或者轉碼成Gif,解碼以及轉碼後的數據我們稱其爲Resource。現在面臨的問題還是一樣的:
由於框架的設計決定了需要解碼的格式是不定的,要轉碼的格式也是不定的,如何高效的組織這個過程呢?
這個和Request被加載的過程類似,這裏採用的是模板方法設計模式:

可以看到,這裏我們能從Registry中獲取所有的ResourceDecoder和ResourceTranscoder,然後判斷哪個解碼器或轉碼器適合當前格式,直接調用相關的decode和transcode方法就可以了。
以這種方式,我們能隨意擴展不同格式的解碼和轉碼了。

4. 從Resource到Resource的設計——資源變換操作

資源解碼並轉碼後,由於某些特殊的需求,我們是不能直接使用的,比如有圓角需求,透明度需求等等,完成這步轉換的,就是Transformation。由於這一步轉換是可選的,和上面兩步都不同必須進過的步驟不同,這裏的Transformation就不能存在一個地方主動去取,必須是得構建這整個流程的時候指定使用哪個Transformation,這裏沒有什麼複雜的架構,大家瞭解下Transformation的大致情況即可:


5. 從Resource到顯示在Target上的設計——資源顯示操作

現在我們走到了最後一步,需要將符合條件的Resource顯示在指定的Target上,當然具體如何顯示細節本文不討論,我們主要關注的是顯示時候的動畫操作,也就是經過Transition的 transition()。這一步和上面一步類似,是否需要使用Transition和使用哪個Transition都是由請求時用戶決定的,因此這裏也沒有複雜架構,大家看下Transition的組成即可:


6. 總結

其實從總體架構上來說,Glide的設計無疑是非常完美的,每一個步驟都是面對接口編程,可以隨意新增或修改其中的某一步,擴展性非常強,這雖然讓架構變的更加複雜,但這點代價是值得的。以這個架構來說,只要Android不死,Glide都能一直用下去。
最後,本文只是簡單的給大家一個Glide的大致流程,每個流程是怎麼回事,讓大家有個概念,甚至連最複雜的緩存都沒提到,更不用說每一步的具體流程了,針對這些,我將會繼續寫一系列的文章,希望能將Glide說清楚。最後的最後,文章都是我個人的理解,如果有錯誤,懇請大家指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章