? ? Retrofit是一個(gè)Android網(wǎng)絡(luò)框架。是一個(gè)對(duì)OKHttp框架的簡(jiǎn)單封裝。所以其內(nèi)部實(shí)現(xiàn)原理實(shí)際上也是基于OkHttp的請(qǐng)求方式的。Retrofit采用注解方式開(kāi)發(fā)。通過(guò)注解構(gòu)建不同的請(qǐng)求和請(qǐng)求的參數(shù),省去了創(chuàng)建大量類(lèi)似的請(qǐng)求與方法,實(shí)際上這些參數(shù)最終都會(huì)在OkHttp中組合成一個(gè)完整的Http的請(qǐng)求(包括請(qǐng)求的頭和請(qǐng)求體),并通過(guò)OkHttp框架進(jìn)行發(fā)送。下面將簡(jiǎn)單介紹一下Retrofit的內(nèi)部實(shí)現(xiàn)以及使用。
Retrofit引入
在Android Studio中添加Retrofit引用
在Android Studio中使用Retrofit提供的框架,需要引用Retrofit。引用的方式為在app的build.gradle中的dependencies中添加引用項(xiàng),如下所示:

上述引用的是retrofit2.2版本的Jar包,這樣Android
Studio將自動(dòng)下載Retrofit的Jar包以及其所引用的Jar包(OkHttp的Jar包)。
Retrofit相關(guān)類(lèi)

上圖為Retrofit包所包含的類(lèi),其中http中包含了有關(guān)注解的定義。包括Body、DELETE、Field、FieldMap、FormUrlEncoded、GET、HEAD、Header、HeaderMap、Headers、HTTP、Multipart、OPTIONS、Part、PartMap、PATCH、Path、POST、PUT、Query、QueryMap、QueryName、Streaming、Url。其中較為常用的有GET、POST、Url、Streaming、Header等。
后面將會(huì)談到這些注釋的用法,暫時(shí)可看成是一種聲明,這種聲明可以針對(duì)方法,也可以針對(duì)方法中的參數(shù)。
Retrofit使用舉例及說(shuō)明
假設(shè)現(xiàn)在有一個(gè)需求,要從一個(gè)Url指定的地址中下載一個(gè)文件(比較通常的情況)。首先,要?jiǎng)?chuàng)建一個(gè)接口,接口名稱(chēng)取為ApiService(可隨便?。?,并且定義一個(gè)接口方法:

這個(gè)方法有幾個(gè)地方需要注意:方法上方的注解@Streaming,@GET;方法的返回值Call,還有這個(gè)泛型的實(shí)例類(lèi)型ResponseBody。
創(chuàng)建了接口之后,就可以創(chuàng)建Retrofit實(shí)例:

Retrofit的創(chuàng)建與接口方法的返回值有關(guān),Retrofit.Builder()可調(diào)用addCallAdapterFactory方法和addCoverterFactory方法,都是針對(duì)返回值的一些轉(zhuǎn)換。所以現(xiàn)在這個(gè)Retrofit的創(chuàng)建是最簡(jiǎn)單的一種形式。其他形式在后面會(huì)再作介紹。
另外值得注意的是baseUrl這個(gè)方法。其參數(shù)是一個(gè)Url字符串,而且必須以“/”作為結(jié)尾,這個(gè)字符串與上面@GET注解的參數(shù)“file/test.txt”共同組成一個(gè)完整的URL,downloaFile()方法將要訪問(wèn)的就是“https://api.git.com/file/test.txt”這個(gè)URL。Retrofit的這種設(shè)定將網(wǎng)絡(luò)文件的域和文件在域中的路徑作了分離,對(duì)同一個(gè)域中的多個(gè)文件訪問(wèn)有一種比較統(tǒng)一的形式。但對(duì)于動(dòng)態(tài)獲得的URL訪問(wèn)則會(huì)存在一點(diǎn)問(wèn)題,因?yàn)閎aseUrl不能以完整的URL作為參數(shù)傳入,必須只傳一個(gè)域(因?yàn)楸仨氁浴?”作為結(jié)尾,不然報(bào)錯(cuò))。幸好Retrofit還提供了一個(gè)注解@URL。只要在方法中聲明一個(gè)參數(shù),并加上@URL注解,就可以動(dòng)態(tài)地將完整的URL作為請(qǐng)求的參數(shù)。如下所示:

這樣,無(wú)論BaseUrl傳入什么參數(shù),都不起作用,而是用downloadFile的參數(shù)fileUrl作為請(qǐng)求的URL。
獲得retrofit實(shí)例之后,就需要獲得接口實(shí)例:

得到實(shí)例后,就可以調(diào)用接口的方法來(lái)獲取Call對(duì)象。即:

整體流程如下圖:

獲得了這個(gè)call對(duì)象就可以開(kāi)始訪問(wèn)網(wǎng)絡(luò)了。Call接口提供enqueue(Callback callback)方法,其中泛型T即Call的泛型,在這里就是ResponseBody。Callback接口包含兩個(gè)方法,OnResponse和onFailure,分別在請(qǐng)求成功和失敗的時(shí)候回調(diào)。于是call調(diào)用的形式如下:

注意上述下載方法省略了,并且inputStream也沒(méi)進(jìn)行相關(guān)的try-catch處理。只是為了介紹retrofit的使用,實(shí)際上獲取了輸入流,就可以從流中讀取文件的內(nèi)容。
總結(jié)一下這個(gè)流程:
1.創(chuàng)建一個(gè)接口,以及接口的方法,使用注解標(biāo)識(shí)該方法的請(qǐng)求參數(shù)。
2.創(chuàng)建Retrofit實(shí)例。
3.通過(guò)retrofit實(shí)例創(chuàng)建接口實(shí)例,調(diào)用接口實(shí)例的接口方法,獲得call對(duì)象。
4.調(diào)用call對(duì)象的enqueue方法,并且傳入Callback參數(shù),實(shí)現(xiàn)onResponse方法和onFailure方法,表示請(qǐng)求成功和失敗時(shí)候的回調(diào)。
Retrofit內(nèi)部實(shí)現(xiàn)說(shuō)明
以上面的例子作為分析對(duì)象,簡(jiǎn)要分析一下Retrofit的內(nèi)部實(shí)現(xiàn)。
上面的例子可能會(huì)有點(diǎn)讓人疑惑,創(chuàng)建的接口ApiService怎么使用Retrofit的create方法就可以創(chuàng)建一個(gè)ApiService的實(shí)例,并且可以調(diào)用其沒(méi)有經(jīng)過(guò)我們具體實(shí)現(xiàn)的方法downloadFile呢?這里使用了反射和代理的設(shè)計(jì)模式,ApiService的實(shí)例實(shí)際上Retrofit創(chuàng)建的一個(gè)代理,而接口方法downloadFile的實(shí)現(xiàn)其實(shí)是經(jīng)過(guò)Retrofit內(nèi)部特殊處理的,個(gè)人覺(jué)得可以這樣理解,這個(gè)downloadFile可以看成是一個(gè)標(biāo)識(shí),是Retrofit內(nèi)部一段邏輯的調(diào)用接口,其方法參數(shù)都需要添加注解,因此不是隨意的想要添加任意參數(shù)都可以的。這個(gè)接口ApiService看起來(lái)是我們自己創(chuàng)建的,連接口方法也是我們定義的,實(shí)際上,它只是為了聲明我們對(duì)網(wǎng)絡(luò)請(qǐng)求的一個(gè)簡(jiǎn)化形式,這需要跟以往創(chuàng)建接口并自己實(shí)現(xiàn)的思路區(qū)分開(kāi)來(lái)。這個(gè)接口的方法不需要我們實(shí)現(xiàn),它已經(jīng)在Retrofit內(nèi)實(shí)現(xiàn)好了,我們只需要添加參數(shù),就可以調(diào)用網(wǎng)絡(luò)請(qǐng)求。下面先來(lái)看這個(gè)代理和方法是怎樣實(shí)現(xiàn)的。
先來(lái)看一下Retrofit的創(chuàng)建,它是通過(guò)內(nèi)部類(lèi)Builder的build()方法創(chuàng)建的,build()方法代碼如下:

Retrofit的構(gòu)造方法如下:

就是一些對(duì)Retrofit內(nèi)參的賦值。上面代碼比較簡(jiǎn)單,下面來(lái)看Retrofit的create方法:

上面的代碼主要關(guān)注粗體部分。當(dāng)調(diào)用接口方法時(shí),方法的實(shí)現(xiàn)就在這里。聯(lián)系上面的例子,接口方法是需要返回Call這個(gè)接口對(duì)象的。而OkHttpCall則是Call的實(shí)現(xiàn)類(lèi),注意到返回的是callAdapter.adapt(okHttpCall),而當(dāng)我們沒(méi)有添加callAdapter的時(shí)候,使用Retrofit默認(rèn)的callAdapter,而這個(gè)默認(rèn)的callAdapter實(shí)際上就是返回傳入的okHttpCall。因此,方法最后返回的即創(chuàng)建的okHttpCall。callAdapter就是對(duì)返回的Call對(duì)象的一個(gè)轉(zhuǎn)化類(lèi),在后面會(huì)談到RxJava和Retrofit的結(jié)合使用,就需要將這個(gè)okHttpCall轉(zhuǎn)換成Rxjava的Observable,這個(gè)時(shí)候就需要使用RxJava的callAdapter,對(duì)call進(jìn)行轉(zhuǎn)化。
loadServiceMethod會(huì)對(duì)method(即我們定義的接口方法)進(jìn)行處理,構(gòu)成ServiceMethod對(duì)象,這個(gè)對(duì)象在OkHttpCall進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí)會(huì)用到,使用ServiceMethod的內(nèi)參組成網(wǎng)絡(luò)請(qǐng)求。Retrofit內(nèi)部使用serviceMethodCache這個(gè)表緩存serviceMethod,如果從serviceMethodCache中沒(méi)有method對(duì)應(yīng)的serviceMethod,則會(huì)通過(guò)ServiceMethod的Buidler創(chuàng)建一個(gè)。
ServiceMethod的創(chuàng)建是通過(guò)Buidler生成的,主要是生成一些內(nèi)參:

這些參數(shù)大部分都是在接口方法中定義的注解以及參數(shù)類(lèi)型獲得的,比如像上例中定義了一個(gè)@GET注解的方法,那么ServiceMethod就會(huì)解釋到這個(gè)@GET注解,并得出這個(gè)方法的httpMethod為GET類(lèi)型。由于對(duì)注解以及參數(shù)類(lèi)型的解釋代碼篇幅較長(zhǎng),這里不一一列出來(lái)。ServiceMethod可以看成是Method經(jīng)過(guò)參數(shù)解釋的參數(shù)結(jié)合,它沒(méi)有真正的網(wǎng)絡(luò)處理邏輯,但對(duì)參數(shù)類(lèi)型已經(jīng)作了相關(guān)的解釋?zhuān)骱昧苏{(diào)用的前置準(zhǔn)備(注意參數(shù)還沒(méi)準(zhǔn)備好,只是參數(shù)類(lèi)型作了解釋?zhuān)驗(yàn)檫@個(gè)時(shí)候真實(shí)的參數(shù)還沒(méi)有傳入,在Retrofit的create方法中可以看到,參數(shù)args是傳給了OkHttpCall中的,不需要傳給ServiceMethod)。
接著來(lái)看接口方法的返回值,即Call對(duì)象。Call是一個(gè)接口,其實(shí)現(xiàn)類(lèi)為OkHttpCall。上面說(shuō)過(guò)如果沒(méi)有添加CallAdapter,那最終返回的就是這個(gè)OkHttpCall。OkHttpCall的構(gòu)造方法也比較簡(jiǎn)單,就是保存了前面獲得的ServiceMethod以及調(diào)用接口方法時(shí)傳入的參數(shù),上面的例子中的downloadFile由于沒(méi)有參數(shù),因此這里傳入的就是空。
如前面例子所示,獲得了Call對(duì)象后,調(diào)用了其enqueue方法,將網(wǎng)絡(luò)請(qǐng)求放入到隊(duì)列中,等待響應(yīng)。enqueue方法如下:


從enqueue方法可知,我們得到的OkHttpCall實(shí)際上是對(duì)OKHttp請(qǐng)求的封裝,實(shí)際的網(wǎng)絡(luò)實(shí)現(xiàn)還是要通過(guò)OKHttp中的相關(guān)類(lèi)來(lái)實(shí)現(xiàn)。這里體現(xiàn)了Retrofit的思想,即對(duì)OkHttp的封裝,以及簡(jiǎn)化其使用。
注意到在OkHttpCall的enqueue方法內(nèi)部,調(diào)用RealCall(OkHttp的相關(guān)類(lèi))的enqueue方法時(shí),同樣會(huì)使用一個(gè)Callback,這個(gè)Callback不同于Retrofit提供的Callbcak(實(shí)際上Retrofit和OkHttp有很多同名類(lèi),需要注意使用的究竟是Retrofit還是OkHttp的,可以使用包名作為區(qū)分)。OkHttp的Call和Response均不支持泛型,它是為了獲得最原始的數(shù)據(jù)而來(lái)的,而Retrofit卻可以對(duì)返回的信息進(jìn)行整理,置換成其他的類(lèi)型(最為典型的就是將數(shù)據(jù)體轉(zhuǎn)成JSON類(lèi)型的數(shù)據(jù))。
簡(jiǎn)單總結(jié)一下Retrofit的主要內(nèi)部實(shí)現(xiàn):
1.Retrofit實(shí)例的構(gòu)造,填充一些參數(shù),比如將在后面談到的callAdapterFactory、convertFactory,以及baseUrl,callFactory(與callAdapterFactory作區(qū)分,若不指定,則為一個(gè)默認(rèn)的OkHttpClient,可以根據(jù)需要?jiǎng)?chuàng)建一個(gè)OkHttpClient,然后對(duì)這個(gè)OkHttpClient添加對(duì)應(yīng)的屬性,以實(shí)現(xiàn)不同的網(wǎng)絡(luò)處理機(jī)制)。
2.Retrofit調(diào)用create創(chuàng)建一個(gè)代理實(shí)體,而代理關(guān)鍵部分在于創(chuàng)建一個(gè)ServiceMethod,ServiceMethod收集接口方法的參數(shù)類(lèi)型以及對(duì)應(yīng)的注解,組成一個(gè)有關(guān)網(wǎng)絡(luò)請(qǐng)求的參數(shù)類(lèi)型集合。
3.ServiceMethod與接口方法傳入的參數(shù)共同作用傳入OkHttpCall,構(gòu)成一個(gè)Call對(duì)象,經(jīng)過(guò)CallAdapterFactory的適配,返回。因此接口方法可以理解為最后獲得這個(gè)Call對(duì)象(真實(shí)來(lái)說(shuō)應(yīng)該是OkHttpCall)。
4.Call對(duì)象調(diào)用enqueue實(shí)際使用的是OkHttp的RealCall的enqueue方法,因此Retrofit的Call對(duì)象可以看成是OkHttp的Call的一個(gè)封裝。當(dāng)然,這不是一個(gè)簡(jiǎn)單的封裝,在網(wǎng)絡(luò)請(qǐng)求獲得響應(yīng)后,OkHttpCall還會(huì)對(duì)返回的信息進(jìn)行轉(zhuǎn)置,根據(jù)ConvertFactory定義的轉(zhuǎn)置模式進(jìn)行轉(zhuǎn)換。