初識Retrofit時总寒,覺得很神奇扶歪,體現(xiàn)在以下兩點:
神奇之一:僅憑注解和接口便可執(zhí)行網(wǎng)絡請求。
public interface GithubService {
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
}
神奇之二:接口方法的返回值變幻無窮摄闸。
無論你是用默認的方式善镰,或者是RxJava,無論是在Java或是Android中調(diào)用年枕,Retrofit均可滿足炫欺,體現(xiàn)在接口的返回值,如下代碼:
public interface GithubService {
//默認方式
@GET("users/{username}")
Call<User> getUserByDefault(@Path("username") String username);
//RxJava方式
@GET("users/{username}")
Observable<User> getUserByRxJava(@Path("username") String username);
}
這兩個神奇之處熏兄,猶如兩層神秘面紗品洛,將Retrofit原本俊俏嬌羞的臉龐,遮掩得若隱若現(xiàn)摩桶。若不揭開面紗桥状,一睹芳容,便無處釋放程序員們最原始的沖動硝清,于是一起來閱讀源碼吧辅斟。
尋找接口的實現(xiàn)類
對于第一個神奇之處,首先我們來看看GithubService接口是如何調(diào)用的:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<User> call = githubService.getUser("geniusmart");
仔細斟酌這段代碼芦拿,GitHubService
本身是個接口砾肺,并調(diào)用了getUser()
方法獲得返回值,那么問題來了:
-
GitHubService
是由我們定義的接口防嗡,但是我們并沒有定義實現(xiàn)類变汪,實現(xiàn)類到底在哪里? - 既然我們沒有定義實現(xiàn)類蚁趁,那么大膽假設該實現(xiàn)類是由Retrofit框架提供的裙盾。對于框架而言,該接口是由開發(fā)者自定義的,框架無法做到未卜先知番官,那么他是如何做到的庐完?
以上兩個問題可以總結(jié)為:框架如何動態(tài)生成GitHubService
的實現(xiàn)類?答案也不難猜測——動態(tài)代理模式徘熔。(對動態(tài)代理有疑問的同學门躯,可以先看下《公共技術點之 Java 動態(tài)代理》。
接下來驗證我們的猜測酷师,GitHubService
的來源在此行代碼中:
GitHubService service = retrofit.create(GitHubService.class);
查看create()
的源碼讶凉,所有謎底都將揭開:
這個方法生成并返回了GitHubService
的動態(tài)代理對象,在執(zhí)行接口的每個方法時山孔,實際執(zhí)行的是invoke()
懂讯,而該方法里的邏輯便是此框架的主體流程,如下代碼:
public Object invoke(Object proxy, Method method, Object... args){
//1.負責注解的解析
ServiceMethod serviceMethod = loadServiceMethod(method);
//2.負責與OKHttp3的對接台颠,處理同步和異步發(fā)送網(wǎng)絡請求
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//3.第二層面紗褐望,下文再做解釋
return serviceMethod.callAdapter.adapt(okHttpCall);
}
這個方法里,做了三件事情:
- 解析方法的注解串前,比如
get/post瘫里、url、Headers
等荡碾,解析的結(jié)果存放于ServiceMethod
對象中谨读。 - 創(chuàng)建
OkHttpCall
,該對象負責與OKHttp3對接玩荠,處理同步或異步的網(wǎng)絡請求漆腌。 - 這是第二層神秘面紗的謎底贼邓,下文再做解釋阶冈。
至此,我們揭開了Retrofit的第一層神秘面紗塑径,這是一位簡單而優(yōu)雅的小女子女坑,表面樸實無華,恬靜清新统舀,令人迫不及待想要揭開她的第二層面紗匆骗,知曉她的內(nèi)心世界。
變幻無窮的適配
首先來回顧下上文提到的第二個神秘之處誉简,同樣是獲取用戶信息的網(wǎng)絡請求碉就,可以返回Call<User>
或 Observable<User>
,這是如何做到的闷串?
上文中瓮钥,invoke()
做的第三件事情便是獲取返回值,如下:
return serviceMethod.callAdapter.adapt(okHttpCall);
其中,adapt
是適配的意思碉熄,其目的是變廢為寶桨武,將指定的輸入類型適配成實際需要的輸出類型,來查看下它的源碼:
public interface CallAdapter<T> {
<R> T adapt(Call<R> call);
}
我們重點關注下adapt
的輸入輸出锈津。這里的設計非常大膽呀酸,輸入是有約束的,即 Call
類型琼梆,而輸出為泛型T
性誉,也就是說輸出是沒有任何約束的,如此一來叮叹,擴展性極強艾栋,可以指定任何的類型。
剩下來的問題便是:
-
adapt
由哪個對象觸發(fā)蛉顽,即CallAdapter
的具體實現(xiàn)是什么蝗砾? - 輸入類型
Call
的具體實現(xiàn)是什么? - 輸出類型
T
的具體實現(xiàn)是什么携冤?
這里直接給出答案悼粮,如下兩圖:
- 通過上面兩圖,可以看到曾棕,輸入對象是固定的扣猫,即
OkHttpCall
對象,網(wǎng)絡請求實際上便是由該對象發(fā)起的翘地。 - 默認方式的輸出對象是
ExecutorCallbackCall
申尤,它持有OkHttpCall
對象,指定了網(wǎng)絡請求執(zhí)行結(jié)束后的回調(diào)函數(shù)在UI線程中執(zhí)行衙耕。 - RxJava方式的輸出對象是
Observable
昧穿,持有該對象便具備函數(shù)式編程的能力。 -
adapt()
的調(diào)用者——適配器CallAdapter
是動態(tài)配置的橙喘,默認方式無需配置时鸵,如果使用RxJava,則需要配置適配器的工廠對象厅瞎,如下代碼:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
這樣設計簡直是解耦得一塌糊涂饰潜,我們可以任意變換調(diào)用方式,也可以任意擴展CallAdapter
來定義新的調(diào)用方式和簸,而框架無需做任何調(diào)整彭雾。比如JakeWharton前幾天開源的RxJava2的適配——retrofit2-rxjava2-adapter,比如針對Google函數(shù)式編程庫 Agera的適配——retrofit-agera-call-adapter锁保,簡直O(jiān)CP得一塌糊涂薯酝。
至此南誊,第二層神秘面紗緩緩而落,這是一位心靈手巧的奇女子蜜托,你給她一針一線抄囚,她還你一幅刺繡,刺繡上可以是錦繡山河橄务,也可以是紫氣東來幔托;你給她新鮮的食材,她變幻出色香味俱全的美味佳肴蜂挪。
總結(jié)
動態(tài)代理重挑、適配是Retrofit的核心,理解清楚這兩點棠涮,再去梳理框架的其他細節(jié)谬哀,方可事半功倍。除此之外严肪,框架內(nèi)部使用了大量的Builder模式和Factory模式史煎,這兩個模式使得創(chuàng)建對象和獲取對象更有條理,更清晰易懂驳糯。值得一提的是篇梭,這個框架配備了完整的單元測試用例,非常值得我們學習酝枢。
參考文章
http://square.github.io/retrofit/
https://github.com/square/retrofit