Android Glide4 源碼解析--框架初始化

原文: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0403/9555.html

這里添加了一些自己的閱讀筆記.

版本 4.x

一、前言

在眾多的圖片加載框架中,Glide是Google推薦的,并在自家的項目中大量使用的一個非常強大的框架,專注于平滑滾動,并且還提供Gif,本地Vedio首幀的解碼和顯示。Glide提供了非常便捷的鏈式調用接口,以及豐富的拓展和自定義功能,開發者可以非常簡單地對框架進行配置和圖片再加工。

如今Gilde已經更新到4.x,了解其源碼對更好的使用Glide,以及學習相關的圖片處理技術,學習更優雅的編碼會有很大的幫助。

再github上放了一個最簡單的工程, 可以直接拿來研究代碼
https://github.com/shaopx/GlideApp

當然你要是不嫌麻煩可以去官方地址: 要想正常編譯還是要費點勁的.
https://github.com/bumptech/glide

不得不說,Glide整個框架的極其復雜的,特別是在對資源的轉換和解碼過程中,涉及了許多的嵌套循環,同時也使用了大量的工廠模式用于生產轉換模塊,編碼模塊,解碼模塊等,筆者在閱讀過程中,多次迷失在茫茫的代碼流中。

為此,萌生了將對Glide的理解記錄成文的想法,借以理清思路,也希望這一系列的文章可以幫助到無論是了解,還是準備閱讀Glide源碼的你,稍微理清一些思路。如有不對的地方,歡迎指正~

那么接下來,我們就先看看Glide是如何進行框架初始化的。

注意:本文源碼版本為v4.6.1,不同版本可能存在一些差異!

二、Glide.with發生了什么?

1. Glide單例的加載

使用過Glide的都知道,調用Glide加載一張圖片時,第一句代碼便是Glide.with(this),這里肯定就是Glide的入口了,通過這句代碼,Glide開始了“漫漫的”初始化之路。

Glide重載了多個with的方法,分別用于不同的情境下使用,我們看其中最常用的在Activity中調用的方法,即

image

首先,跟進getRetriever(activity)

image

這里首先檢查了context是否為空,如果為null,拋出異常。

我們重點來看Glide.get(context)

image

這里是一個典型的雙檢鎖單例模式。

繼續跟進checkAndInitialzeGlide(context)

image
image

注意這里,在最后注入了一個GlideBuilder,這個就是Glide的建造器,用于構建Glide的一些參數和配置。

最后,來到真正初始化Glide的方法(代碼去除了一些Log打印)。

image

留意最后將初始化得到的glide賦值給了Glide.glide的單例。

接下里就來看看在這初始化方法中,Glide都加載了哪些配置。

2. GlideModule配置加載

在使用Glide的時候,我們都會有一些想要設置的系統級配置,如設置緩存的存儲位置,緩存區的大小,網絡加載模塊等等,那么我們通常就是使用GldieModule進行配置。在Glide3.x中,我們首先會定義一個繼承于GlideModule的類,然后在項目的AndroidMenifest.xml中進行指定:

1.  `<meta-data  android:name="com.test.GlideConfiguration"`
2.  `android:value="GlideModule"/>`

而在Glide4中,提供另外一個配置的模式,那就是注解,并且不再繼承GlideModule,而是繼承AppGlideModule和LibraryGlideModule,分別對應Application和Library,使用@GlideModule注解進行標記。而Glide3.x中的配置方式已經建議放棄使用。


    @GlideModule
    public class GlideConfiguration extends AppGlideModule {
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
            //設置緩存到外部存儲器
            builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context)); 
        }
    }

那么,Glide是如何對GlideModule的配置進行初始化的呢?

image

第二行代碼中,getAnnotationGeneratedGlideModules()會獲取Glide注解自動生產的一個Glide的Module配置器。如下:

image

其中‘com.bumptech.glide.GeneratedAppGlideModuleImpl’是在編譯時由Glide生成的一個類,主要用于過濾不必要的GlideModule,以及提供一個請求檢索器工廠,這個后面會講到。

接下生成一個Manifest解析器ManifestParser,用于獲取配置的GlideModule,并存放在manifestModules中。然后是一個判斷


    if (annotationGeneratedModule != null
            && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
        ......
    }

如果條件成立,即編譯時自動生成的類中,包含了需要排除的GlideModule,逐個將其移除。

接著以上代碼,Glide將逐個調用剩下的GlideModule,并回調applyOptions和registerComponents接口,這時,用戶配置的GlideModule就會被調用,同時用戶設置的參數也就被配置到Glide中。

image

在以上代碼中,發現一句代碼,在回調registerComponents前,首先構建了glide的實例。

這是一句非常重要的代碼,整個Glide框架最重要的初始化內容都在其中實現。


1.  `Glide glide = builder.build(applicationContext);`

3. GlideBuilder構建Glide單例

跳轉到GlideBuilder中,看build方法做了哪些事情。代碼并不復雜,直接看代碼中的注釋。

image

通過以上一系列工具的新建,Glide建立了資源請求線程池,本地緩存加載線程池,動畫線程池,內存緩存器,磁盤緩存工具等等,接著構造了Engine數據加載引擎,最后再將Engine注入Glide,構建Glide。

其中還建立了一個請求器索引器,用于索引RequestManger,后面我們再詳細講。

我們進入最后, 構建Glide。

4. 構建Glide,配置數據轉換器/解碼器/轉碼器/編碼器

回到Glide中,看看Glide的構造函數,這是一個長得變態的構造函數(有200行),但是不必被它嚇倒(好吧,其實第一次看到這里,我是被嚇倒了,直接略過去了,限于文章篇幅,這里只截取了部分源碼,仔細的話可以直接看源碼),仔細分析一下,其實整個構造過程并沒那么復雜。

image

其中最重要的是步驟3和步驟4,分別為Glide初始化了模型轉換加載器,解碼器,轉碼器,編碼器,并將對各種類型進行一一注冊,將其列成表格如下:

  • 模型轉換器
轉換器 功能
ResourceLoader.StreamFactory 將Android資源ID轉換為Uri,在加載成為InputStream
ResourceLoader.UriFactory 將資源ID轉換為Uri
ResourceLoader.FileDescriptorFactory 將資源ID轉化為ParcelFileDescriptor
ResourceLoader.AssetFileDescriptorFactory 將資源ID轉化為AssetFileDescriptor
UnitModelLoader.Factory 不做任何轉換,返回源數據
ByteBufferFileLoader.Factory 將File轉換為ByteBuffer
FileLoader.StreamFactory 將File轉換為InputStream
FileLoader.FileDescriptorFactory 將File轉化為ParcelFileDescriptor
DataUrlLoader.StreamFactory 將Url轉化為InputStream
StringLoader.StreamFactory 將String轉換為InputStream
StringLoader.AssetFileDescriptorFactory 將String轉換為AssetFileDescriptor
HttpUriLoader.Factory 將http/https Uri轉換為InputStream
UriLoader.StreamFactory 將Uri轉換為InputStream
UriLoader.FileDescriptorFactory 將Uri轉換為ParcelFileDescriptor
UriLoader.AssetFileDescriptorFactory 將Uri轉換為AssetFileDescriptor
UrlUriLoader.StreamFactory 將將http/https的Uri轉換為InputStream
UrlLoader.StreamFactory 將Url轉換為InputStream
HttpGlideUrlLoader.Factory 將HttpGlide轉換為InputStream
...... ......
  • 解碼器
解碼器 功能
ByteBufferGifDecoder 將ByteBuffer解碼為GifDrawable
ByteBufferBitmapDecoder 將ByteBuffer解碼為Bitmap
ResourceDrawableDecoder 將資源Uri解碼為Drawable
ResourceBitmapDecoder 將資源ID解碼為Bitmap
BitmapDrawableDecoder 將數據解碼為BitmapDrawable
StreamBitmapDecoder 將InputStreams解碼為Bitmap
StreamGifDecoder 將InputStream數據轉換為BtyeBuffer,再解碼為GifDrawable
GifFrameResourceDecoder 解碼gif幀
FileDecoder 包裝File成為FileResource
UnitDrawableDecoder 將Drawable包裝為DrawableResource
UnitBitmapDecoder 包裝Bitmap成為BitmapResource
VideoDecoder 將本地視頻文件解碼為Bitmap
  • 轉碼器
轉碼器 功能
BitmapDrawableTranscoder 將Bitmap轉碼為BitmapDrawable
BitmapBytesTranscoder 將Bitmap轉碼為Byte arrays
DrawableBytesTranscoder 將BitmapDrawable轉碼為Byte arrays
GifDrawableBytesTranscoder 將GifDrawable轉碼為Byte arrays
  • 編碼器
編碼器 功能
ByteBufferEncoder 將Byte數據緩存為File
StreamEncoder InputStream緩存為File
BitmapEncoder 將Bitmap數據緩存為File
BitmapDrawableEncoder 將BitmapDrawable數據緩存為File
GifDrawableEncoder 將GifDrawable數據緩存為File
  • 模型轉換注冊表(實在太多,只列出了部分)
源數據 轉換數據 轉換器
Integer.class InputStream.class ResourceLoader.StreamFactory
Integer.class ParcelFileDescriptor.class ResourceLoader.FileDescriptorFactory
...... ...... ......
String.class InputStream.class DataUrlLoader.StreamFactory
String.class InputStream.class StringLoader.StreamFactory
...... ...... ......
Uri.class InputStream.class DataUrlLoader.StreamFactory
Uri.class InputStream.class HttpUriLoader.Factory
Uri.class InputStream.class UriLoader.StreamFactory
URL.class InputStream.class UrlLoader.StreamFactory
...... ...... ......

以上模型轉換注冊表非常重要,在Glide進入解碼流程時,將會遍歷這里注冊的所有可能轉換的情形,嘗試進行數據轉換。

這里只列出部分情形,其它還包括File/Bitmap/Drawable/Byte等等幾乎涵括了日常使用的情況。

Glide的加載流程可以概括為以下流程:

model(數據源)-->data(轉換數據)-->decode(解碼)-->transformed(縮放)-->transcoded(轉碼)-->encoded(編碼保存到本地)

其中,transformed為對解碼得到的圖片數據進行縮放,如FitCenter、CropCenter等。

到這里,Glide單例就構建完成了,讓我們返回到Glide#with中

image

在構建好Glide后,通過getRequestManagerRetriever()將會得到一個RequestManagerRetriever,即RequestManager的檢索器,RequestManagerRetriever#get()將為每個請求頁面創建一個RequestManager。

還記得GlideBuilder#build提到的一句代碼嗎?


1.  `RequestManagerRetriever requestManagerRetriever =`
2.  `new  RequestManagerRetriever(requestManagerFactory);`

沒錯,這里獲取的就是它。這里就必須要講到Glide數據請求的生命周期了。

我們都知道Glide會根據頁面的生命周期來自動的開啟和結束數據的請求,那么Glide是怎么做到的呢?

5. 生命周期管理

我們進入RequestManagerRetriever#get(Activity)方法中。

image

首先,判斷是否為后臺線程,如果是,則使用ApplicationContext重新獲取。
重點來看else代碼塊。先斷言請求的頁面是否已經銷毀。否則獲取當前頁面的FragmentManager,并傳給fragmentGet方法。

image

在fragmentGet中首先通過getRequestManagerFragment()來獲取一個命名為FRAGMENT_TAG的fragment,如不存在,則新建一個RequestManagerFragment,并添加到當前頁面中。

這里我們就可以猜到了,Glide是通過在頁面中添加一個Fragment來動態監聽頁面的創建和銷毀,從而達到依賴頁面生命周期,動態管理請求的目的。

image

在RequestManagerFragment構造函數中,注入了一個生命周期監聽器ActivityFragmentLifecycle,并在Fragment各個生命周期回調中,調用了對應的方法。

而ActivityFragmentLifecycle也緊接著會調用lifecycleListener監聽器,而這個監聽器其實就是RequestManger。如下:

image

最后,RequestManagerRetriever#fragmentGet,判斷這個Fragment的RequestManager是否存在,否則創建一個RequestManager,并將生命周期注入,同時RquestManager構建時,將會通過addListener注入生命周期回調(具體可以查看RequestManger構造函數)。

最后,Glide#with終將得到一個RequestManager。

至此,Glide的加載過程就解析完畢了。總結一下整個流程:

  • 通過AndroidManifest和@GlideModule注解獲取用戶自定義配置GlideModule,并調用其對應的方法
  • 通過GlideBuilder構建Glide:
    1.新建線程池
    2.新建圖片緩存池和緩存池
    3.新建內存緩存管理器
    4.新建默認本地緩存管理器
    5.新建請求引擎Engine
    6.新建RequestManger檢索器
    7.新建Glide
  • Glide構造方法中,新建模型轉換器,解碼器,轉碼器,編碼器,以及生成Glide上下文GlideContext
  • 通過RequestManager檢索器,建立生命周期監聽,并建立一個RequestManager
  • 完成!

三、 Glide與GlideApp

如果在項目中已經使用了Glide3.x,并且想要升級到Glide4.x,那么你會發現,原來使用鏈式調用進行參數配置的方法已經被修改了,同一個封裝到了RequesOptions中,如下:

    RequestOptions options = new RequestOptions()
            .centerCrop()
            .placeholder(R.mipmap.ic_launcher_round)
            .error(R.mipmap.ic_launcher)
            .priority(Priority.HIGH)
            .diskCacheStrategy(DiskCacheStrategy.NONE);
    Glide.with(this)
            .load(ImageConfig.URL_GIF)
            .apply(options)
            .into(iv);

這樣的話升級后將導致大量的修改,當然你也可以自己封裝一下,但是Glide已經為我們做好了兼容方案。

還記得初始化是通過@GlideModule注解來注冊自定義配置嗎?只要在項目中定義這么一個配置,那么Glide將會自動幫我們生成一個GlideApp模塊,封裝了Glide3.x中的調用方式。


    public class GlideConfiguration extends AppGlideModule {
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
        }
    }

調用如下,還是原來的配方,還是熟悉的味道~


    GlideApp.with(this)
            .load(ImageConfig.URL_WEBP)
            .sizeMultiplier(0.5f)
            .centerCrop()
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .error(R.mipmap.ic_launcher)
            .into(iv);

如果你還覺得不爽,那么你甚至可以把GlideApp直接修改為Glide,實現幾乎“無縫對接”。當然,你還是要修改引用路徑的。


    @GlideModule(glideName="Glide")
    public class GlideConfiguration extends AppGlideModule {
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
        }
    }

以上,就是Glide4初始化的源碼解析了

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容