天天看點

Glide加載圖檔流程(Part One)

注意

為了讓朋友們盡快的了解glide加載圖檔的流程,我們就從最簡單的方式入手。請看如下代碼:

代碼清單1-1

使用glide加載圖檔就是這麼簡單!!!

在上述代碼清單1-1中,我是在fragmentactivity子類中調用了glide的<code>with</code>方法,so這裡的<code>this</code>即是指<code>fragmentactivity</code>的子類執行個體。我們點選檢視其源碼,如下:

首先擷取一個<code>requestmanagerretriever</code>執行個體,通過該執行個體得到<code>requestmanager</code>對象。我們現在進入<code>requestmanagerretriever#get(fragmentactivity)</code>方法中來具體看看<code>requestmanager</code>的擷取過程:

我們隻考慮正常情況下使用的場景(不考慮緩存等情況,後文與此相同),由上面的代碼,我們很容易定位到<code>supportfragmentget</code>方法:

由于我們是初次調用glide加載圖檔,是以這裡會建立一個新的<code>requestmanager</code>執行個體,并存入<code>supportrequestmanagerfragment</code>的<code>current</code>執行個體中。傳入<code>requestmanager</code>對象中的<code>lifecycle</code>和<code>requestmanagertreenode</code>我們後面再說。這裡需要注意的是,在建立<code>requestmanager</code>對象時,預設建立了一個<code>requesttracker</code>對象和<code>optionsapplier</code>對象,以及我們的<code>glide</code>對象。

我們進入到<code>requestmanager</code>的構造函數中,定位到<code>glide</code>執行個體的擷取過程:

在上述代碼中,我們順藤摸瓜,來到<code>glide</code>類的<code>get</code>方法中:

在擷取<code>glide</code>的執行個體過程中,我們看到glide會從androidmanifest.xml檔案中查詢我們注冊的<code>glidemodule</code>。同時将<code>glidebuilder</code>和<code>glide</code>的執行個體傳入我們注冊的<code>glidemodule</code>的對應方法中。由此我們可以了解到,如果想預先控制<code>glide</code>的一些行為,可以在我們的<code>glidemodule</code>的<code>applyotions</code>和<code>registercomponents</code>方法中做控制。

接下來我們繼續檢視<code>glidebuilder</code>的<code>createglide</code>方法,直接定位到方法的最後一行:

好了,重頭戲該上演了,我們來看看<code>glide</code>類的構造函數:

這個方法相對比較長,但是了解起來較為簡單。主要就是注冊一些dataloadprovider、modelloader的factory以及幾個transcoder。為了更直覺的了解和查詢,我将它們羅列成下面三張表:(ps:這三張表對于了解glide的加載流程很重要)

表1-1 modeltype到datatype的映射表

id

modelclass

resourceclass

dataloaderfactory

1

file

parcelfiledescriptor

filedescriptorfileloader.factory

2

inputstream

streamfileloader.factory

3

int

filedescriptorresourceloader.factory

4

streamresourceloader.factory

5

integer

6

7

string

filedescriptorstringloader.factory

8

streamstringloader.factory

9

uri

filedescriptoruriloader.factory

10

streamuriloader.factory

11

url

streamurlloader.factory

12

glideurl

httpurlglideurlloader.factory

13

byte[]

streambytearrayloader.factory

14*

[modeltype]

imagevideowrapper

imagevideomodelloader

注 編号( 14* )并不是在構造方法中直接注冊的 factory 而是在 <code>drawablerequestbuidler</code>中建立的一個 modelloader 。

表1-2 datatype到resourcetype的映射表

dataclass

dataloadprovider

bitmap

streambitmapdataloadprovider

filedescriptorbitmapdataloadprovider

imagevideodataloadprovider

gifdrawable

gifdrawableloadprovider

gifbitmapwrapper

imagevideogifdrawableloadprovider

streamfiledataloadprovider

表1-3 resourcetype到transcodetype的映射

decodedclass

transcodedclass

resourcetranscoder

glidebitmapdrawable

glidebitmapdrawabletranscoder

glidedrawable

gifbitmapwrapperdrawabletranscoder

上表 标題 提到的 modeltype、datatype、resourcetype和transcodetype在<code>genericrequestbuilder</code>類的注釋中有說明:

對于這四種範型的了解是了解glide運作的關鍵,接口<code>loadprovider</code>範型a、t、z、r與此對應。部落客在這幾個範型種繞了半天。大家隻要參照着上面的三張表來看glide源碼,相信一定會有柳暗花明的感覺 :)

在步驟(一)中我們得到一個<code>requestmanager</code>對象,緊接着我們就來看看<code>requestmanager</code>對象的<code>load(string)</code>方法:

從代碼中我們得知,該方法傳回了一個<code>drawabletyperequest</code>對象,有關<code>drawabletyperequest</code>執行個體的初始化過程,直接上圖,大家參考着上面提供的三張表應該不難了解:

Glide加載圖檔流程(Part One)

注意 了解了上圖的朋友可以直接跳到下一小節,下面的描述很枯燥 :(

該對象的繼承關系為<code>drawabletyperequest</code>–&gt;<code>drawablerequestbuilder</code>–&gt;<code>genericrequestbuilder</code>。這個對象用于加載<code>load(string)</code>參數所指代的内容。順着<code>load(string)</code>方法,我們來重點看看<code>fromstring()</code>–&gt;<code>loadgeneric(class&lt;t&gt; modelclass)</code>方法。由<code>fromstring()</code>我們得知這裡的<code>modelclass</code>即為<code>string.class</code>:

很顯然,上面方法中的泛型<code>t</code>即指代<code>string</code>。

在<code>loadgeneric(class&lt;t&gt; modelclass)</code>方法中,我們來看第一行代碼:

<code>modelloader&lt;t, inputstream&gt; streammodelloader = glide.buildstreammodelloader(modelclass, context);</code>

由<code>buildstreammodelloader</code>方法的注釋我們得知,該方法是用來生成一個<code>modelloader</code>執行個體來用于提供<code>inputstream</code>輸入源。

我們順藤摸瓜,找到了<code>genericloaderfactory</code>類中的<code>buildmodelloader(class&lt;t&gt; modelclass, class&lt;y&gt; resourceclass)</code>方法,該方法中的<code>t</code>與<code>y</code>泛型在我們的調用過程中分别指代<code>string</code>和<code>inputstream</code>類型:

在上述方法中,我們定位到這行代碼:

<code>final modelloaderfactory&lt;t, y&gt; factory = getfactory(modelclass, resourceclass);</code>

在這行代碼中,我們是通過調用<code>getfactory(class&lt;t&gt; modelclass, class&lt;y&gt; resourceclass)</code>方法來産生一個<code>modelloaderfactory</code>執行個體。現在我們就來看源碼:

很顯然,我們是首先從map表<code>modelclasstoresourcefactories</code>中擷取一個已注冊的<code>modelloaderfactory</code>對象。那麼,我們所需要的<code>modelloaderfactory</code>是否已經注冊呢?翻看步驟(一)中提到的表1-1,是不是就是表中的id 8 的内容?對應着條目8,我們獲得了一個<code>streamstringloader</code>對象。後面擷取引用<code>filedescriptormodelloader</code>的情況與此類似。

緊接着就生成了一個<code>drawabletyperequest&lt;string&gt;</code>對象,經過<code>optionsapplier</code>對象進一步處理後傳回到了之前<code>requestmanager</code>的<code>fromstring()</code>方法。有關<code>optionsapplier</code>對象的作用,我們在後文會提到。這裡我們隻需簡單認為傳回了一個原生态的<code>drawabletyperequest&lt;string&gt;</code>對象即可。

在建立<code>drawabletyperequest</code>對象時,我們要提到一個重要的類<code>fixedloadprovider</code>(上文已經提及 :) )。這個類執行個體在後面請求網絡圖檔時會用到。在<code>drawabletyperequest</code>的構造函數中,我們是通過該對象的<code>buildprovider</code>方法生成的:

代碼清單1-2

在這個方法中,我們重點留意下<code>transcoder</code>執行個體、<code>dataloadprovider</code>類和<code>imagevideomodelloader</code>類。通過<code>glide</code>的構造函數和表1-2、表1-3,我們很容易得到這裡的<code>transcoder</code>執行個體為<code>gifbitmapwrapperdrawabletranscoder</code>對象(表1-3條目2),<code>dataloadprovider</code>執行個體為類<code>imagevideogifdrawableloadprovider</code>的對象(表1-2條目5),而這裡的<code>imagevideomodelloader</code>就對應了表1-1中的條目(14*)。

有關<code>imagevideogifdrawableloadprovider</code>的來龍去脈,直接看下圖,後面資料流轉圖檔的邏輯就靠它了:)

Glide加載圖檔流程(Part One)

回到<code>requestmanager</code>的<code>load(string string)</code>方法,接下來即是調用<code>drawabletyperequest&lt;string&gt;</code>對象的<code>load(string)</code>方法了。這個方法很簡單,隻是儲存了狀态而已:

現在我們來看看加載圖檔的最後一步<code>into(fullscreenview)</code>。在這裡,<code>fullscreenview</code>是個<code>imageview</code>執行個體。據此我們順着方法的調用定位到了<code>genericrequestbuilder</code>類的<code>into(imageview view)</code>方法:

我們暫且忽略圖檔的變換邏輯,隻看圖檔的加載過程。在步驟(二)中我們很容易确定,該方法中的<code>transcodetype</code>指代<code>glidedrawable</code>類,而<code>transcodeclass</code>即為<code>glidedrawable</code>的一個執行個體(可參看圖1-1)。代碼<code>glide.buildimageviewtarget(view, transcodeclass)</code>傳回了一個<code>glidedrawableimageviewtarget</code>對象。這個類的作用後面在顯示圖檔的時候會用到,這裡暫且不表。下面我們來看方法<code>into(y target)</code>:

代碼清單1-3

這個方法我們需要重點關心的是<code>request</code>類。這個類的具體子類即是根據圖檔路徑來加載圖檔到imageview中。現在我們就來看看這個<code>request</code>的具體執行個體(<code>buildrequest(target&lt;transcodetype&gt; target)--&gt; buildrequestrecursive(target&lt;transcodetype&gt; target, thumbnailrequestcoordinator parentcoordinator)</code>):

我們先忽略縮略圖問題,直接到本方法的最後來看看<code>genericrequest.obtain()</code>方法(<code>obtainrequest()--&gt;genericrequest.obtain()</code>):

這個方法涉及到了很多執行個體對象,而這些執行個體對象中,我們首先需要關注的就是<code>loadprovider</code>和<code>glide.getengine()</code>兩個具體對象。這兩個對象在上面兩個步驟中已經涉及。後面我們将看到它們的具體作用。

擷取到<code>genericrequest</code>對象後,我們再次回到代碼清單1-3的<code>y into(y target)</code>方法中。我們看到有這一行代碼:

<code>requesttracker.runrequest(request);</code>

很顯然,這一行代碼便是要加載我們的圖檔了。按圖索骥,我們來到了<code>genericrequest</code>類的<code>begin()</code>方法。

在該方法中,我們要關注的是該類的<code>onsizeready(int width, int height)</code>方法:

代碼清單1-4

在<code>onsizeready</code>方法中,我們再次看到了<code>loadprovider</code>的影子。在步驟(二)的最後我們提到了<code>fixedloadprovider</code>對象,而這裡的<code>loadprovider</code>其實就是<code>fixedloadprovider</code>類的一個封裝類<code>childloadprovider</code>的執行個體。此時朋友們可能有疑問,在什麼時候<code>fixedloadprovider</code>對象就變成了<code>childloadprovider</code>對象了呢?其實就是在步驟(二)中擷取<code>drawabletyperequest</code>對象時進行了封裝。在<code>drawabletyperequest</code>的祖父類<code>genericrequestbuilder</code>的構造函數中:

<code>this.loadprovider = loadprovider != null ? new childloadprovider&lt;modeltype, datatype, resourcetype, transcodetype&gt;(loadprovider) : null;</code>

看到這裡我們就明白了,在<code>genericrequest</code>的<code>onsizeready</code>方法中(見代碼清單1-4),我們得到的<code>modelloader</code>對象即為步驟(二)中提到的<code>imagevideomodelloader</code>類的執行個體。而這裡的<code>datafetcher</code>對象即為<code>stringloader</code>類(<code>streamstringloader</code>的基類)的方法<code>getresourcefetcher</code>擷取的<code>datafetcher</code>執行個體:

按照步驟(二)中擷取<code>streamstringloader</code>的方式,并結合表1-1,我們一步步得到上述方法中的<code>uriloader</code>執行個體即為<code>httpurlglideurlloader</code>對象。下面我們就來看看類<code>httpurlglideurlloader</code>的<code>getresourcefetcher</code>方法:

由于我們是初次調用glide來加載圖檔,是以會生成一個<code>httpurlfetcher</code>對象。而這個對象即是代碼清單1-4中方法<code>onsizeready</code>的<code>datafetcher</code>對象執行個體<code>datafetcher</code>。

我們接着代碼清單1-4往下看,有這麼一行代碼:

<code>loadstatus = engine.load(signature, width, height, datafetcher, loadprovider, transformation, transcoder, priority, ismemorycacheable, diskcachestrategy, this);</code>

這一行便是加載圖檔并顯示的過程了。我們進到<code>engine</code>的<code>load</code>方法中(這裡的<code>engine</code>執行個體即為上述<code>genericrequest.obtain</code>中傳遞過來的<code>engine</code>對象,也就是在生成<code>glide</code>對象時<code>glidebuilder</code>建立的<code>engine</code>執行個體):

在這個方法中,我們主要關心的是<code>enginerunnable</code>的<code>run</code>方法:

代碼清單1-5

進一步,我們來到<code>decode()</code>方法中:

在<code>enginerunnable</code>的構造方法中,<code>stage</code>預設指派為<code>stage.cache</code>,是以在<code>decode</code>方法中首先會調用<code>decodefromcache</code>方法。很顯然,由于我們是首次加載圖檔,緩存中是沒有的。是以在代碼清單1-5中,我們拿到的<code>resource</code>為null,此時<code>onloadfailed</code>方法會被調起:

在該方法中,<code>stage</code>被重置為<code>stage.source</code>,并将<code>enginerunnable</code>送出給了<code>enginerunnablemanager</code>對象。

從<code>engine</code>的<code>load</code>方法中,我們很容易獲得<code>enginerunnablemanager</code>的具體執行個體為<code>enginejob</code>的對象:

<code>enginejob</code>的<code>submitforsource</code>方法很簡單,隻有一行代碼,即将一個線程放入線程池中等待被調用。這樣,待線程被調起時,我們又回到了代碼清單1-5,此時根據<code>isdecodingfromcache</code>方法傳回false而來到了<code>decodefromsource</code>方法中:

進入到<code>decodejob</code>的<code>decodefromsource</code>方法:

代碼清單1-6

接着我們來檢視<code>decodesource</code>方法:

看到這裡,我們終算拿到了資料源:

<code>final a data = fetcher.loaddata(priority);</code>

這裡的<code>fetcher</code>即是我們上文提到的<code>httpurlfetcher</code>類的執行個體,那麼現在我們就來看看它是如何加載資料源的:

順着該方法走下去,我們便明白了,<code>glide</code>預設加載網絡圖檔是通過<code>httpurlconnection</code>方式。

在這裡,我們拿到的資料源是一個inputstream輸入流。而我們的要看到的是一張張的圖檔。這又是如何擷取的呢?

ok,順着方法往下看,我們來到這行:

進入到<code>decodefromsourcedata</code>方法:

同樣避開緩存不談,直接看下面這行:

追本溯源,這裡的<code>loadprovider</code>還是那個<code>childloadprovider</code>,而它的sourcedecoder即是步驟(二)中圖1-2的<code>imagevideogifdrawableloadprovider</code>的<code>sourcedecoder</code>,即類<code>gifbitmapwrapperresourcedecoder</code>的執行個體。

我們順着<code>gifbitmapwrapperresourcedecoder</code>的<code>decode</code>方法走,我們來到這個方法:

在這個方法中,會根據<code>imageheaderparser.imagetype</code>來決定是解碼為gif圖檔還是bitmap圖檔。這裡我們就先來看看普通的bitmap是如何解碼的。而有關gif圖檔的解碼過程,有興趣的朋友可以看看。後面的系列部落格中我們再來一同研究交流。好了,直接上代碼:

結合着圖1-2,我們定位到了<code>imagevideobitmapdecoder</code>的<code>decode</code>方法中。很顯然,這也是個不幹事兒的主兒,走在一線的是它的<code>streamdecoder</code>。這個成員變量在哪裡初始化的呢?我們回到類<code>imagevideodataloadprovider</code>中,在其構造方法中我們找到了一點線索:

好吧,我們又回到了<code>glide</code>的構造函數中,參照着表1-2和圖1-2來看,這裡的sourcedecoder即是<code>streambitmapdecoder</code>。話不多說,我們來看看該類中是否有我們要找的<code>decode</code>方法吧:

哈哈,我們終于看到了<code>bitmap</code>的身影。進入<code>downsampler</code>中檢視,好吧,這個我們留到後面再說。

拿到bitmap後我們再次回到<code>gifbitmapwrapperresourcedecoder</code>的<code>decodebitmapwrapper</code>方法中,很顯然,這裡對我們的bitmap做了一次封裝,傳回了一個<code>resource&lt;gifbitmapwrapper&gt;</code>對象。帶着這個對象,我們回到代碼清單1-6進入到<code>decodejob</code>的<code>decodefromsource</code>方法中,檢視<code>transformencodeandtranscode</code>方法的調用:

方法中我們主要是關注下面這行:

這裡<code>transcode</code>方法接受的參數<code>transformed</code>是<code>gifbitmapwrapper</code>轉變後的<code>gifbitmapwrapper</code>對象。這裡暫且不表。我們來看<code>transcode</code>方法:

這裡我們又迎來了一個“狠角色”<code>transcoder</code>。為了找到它的來源,我們又回到了建立<code>fixedloadprovider</code>執行個體的地方代碼清單1-2中。這裡用到的<code>transcoder</code>便是那裡獲得到的<code>gifbitmapwrapperdrawabletranscoder</code>類的執行個體。我們來看看它的<code>transcode</code>方法:

從前面我們已經知道,此處的<code>bitmapresource</code>不為<code>null</code>,是以會執行

這裡的<code>bitmapdrawableresourcetranscoder</code>就是哪個呢?呵呵,我們再次來到<code>glide</code>的構造方法中,在方法最後幾行,我們找到了線索:

沒錯,這裡的<code>bitmapdrawableresourcetranscoder</code>便是類<code>glidebitmapdrawabletranscoder</code>的執行個體。我們來一起看看它的<code>transcode</code>方法:

ok,這裡我們拿到一個包裝類<code>glidebitmapdrawableresource</code>。

在拿到<code>glidebitmapdrawableresource</code>後,我們再回到<code>enginerunnable</code>的<code>run</code>方法中(見代碼清單1-5)。有了資料源會調用<code>enginerunnable</code>的<code>onloadcomplete(resource resource)</code>方法:

沿着方法的調用順序,我們回到了<code>genericrequest</code>的<code>onresourceready</code>方法:

這個回調方法中回傳的不再是我們前面擷取到的<code>glidebitmapdrawableresource</code>對象,而是被包裝後的<code>engineresource</code>對象。不過這對我們了解glide的圖檔加載流程沒有任何的影響,依然可以認為是<code>glidebitmapdrawableresource</code>的對象。<code>received</code>是從<code>glidebitmapdrawableresource</code>中擷取到的<code>glidebitmapdrawable</code>。

接着往下看:

在該方法中,我們關注這麼兩行代碼:

這裡的<code>target</code>對象就是本小節一開始提到的<code>glidedrawableimageviewtarget</code>的執行個體。現在我們就來看看它的<code>onresourceready</code>方法:

找到它的基類,我們會發現最終又回到了它的<code>setresource</code>方法:

到這裡,我們glide加載圖檔的主要流程就結束了。其中涉及到的圖檔轉換、緩存、動畫等問題我會在此系列部落格中有所涉及,這裡先暫且不提。

經過上面的三步驟,一張網絡圖檔就加載到了我們的view上。當然,這其中涉及到的内容還有很多,我這裡隻是幫大家梳理下glide加載圖檔的主要流程。現在再回頭對照着步驟(一)中的三張表,相信大家對modeltype、datatype、resourcetype和transcodetype會有更深的了解。從上面的分析中我們也看到了,了解了這四種範型對應的類的功能,也便了解了glide的主要概念和功能。

後面的文章中我們會重點來檢視glide的緩存過程,請大家多多指點多多交流。

繼續閱讀