Retrofit 源碼分析初探
現(xiàn)如今,Android開發(fā)中,網(wǎng)絡(luò)層Retrofit+Okhttp組合好像已成標(biāo)配,身為技術(shù)人員,這么火的框架當(dāng)然得一探究竟,不為裝逼,純粹是為了充電而已。
基本使用介紹
介紹源碼前,我們先看下Retrofit的基本使用,大致了解下流程,跟著這個(gè)流程來分析源碼才不會(huì)亂。
1、初始化Retrofit對(duì)象
Retrofit retrofit = new Retrofit.Builder() //使用自定義的mGsonConverterFactory .addConverterFactory(GsonConverterFactory.create()) .baseUrl("http://apis.baidu.com/txapi/") .build();
2、定義接口
public interface APi { @GET("hello/world") Call<News> getNews(@Query("num") String num,@Query("page")String page); }
3、發(fā)起網(wǎng)絡(luò)請(qǐng)求
mApi = retrofit.create(APi.class); Call<News> news = mApi.getNews("1", "10"); news.enqueue(new Callback<News>() { @Override public void onResponse(Call<News> call, Response<News> response) { } @Override public void onFailure(Call<News> call, Throwable t) { } });
Retrofit本質(zhì)分析
看到上面的整個(gè)流程,不去探究源碼的話肯定一臉懵逼,我就定義了一個(gè)接口,指定了下返回值,為毛這個(gè)接口就可以直接用了?接口的實(shí)現(xiàn)呢?我隨便寫一個(gè)返回值,不指定返回Call行不行?待著這些疑問,我們大致也可以猜出Retrofit是干什么的了。
猜測(cè):Retrofit主要就是為我們定義的接口創(chuàng)造了一個(gè)實(shí)例,然后這個(gè)實(shí)例調(diào)用接口中的方法將我們定義在注解中的值拼裝成發(fā)起http請(qǐng)求所要的信息,最后利用這些信息產(chǎn)生一個(gè)我們?cè)诮涌诜祷刂抵幸?guī)定的對(duì)象,這個(gè)對(duì)象可以用來發(fā)起真正的請(qǐng)求。
簡單的講,Retrofit就是把注解中的東西拼成http請(qǐng)求的對(duì)象,然后由這個(gè)對(duì)象去發(fā)起請(qǐng)求。
驗(yàn)證猜測(cè)
是誰實(shí)現(xiàn)了這個(gè)接口
發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí),有這樣一句:
mApi = retrofit.create(APi.class);
很明顯,接口的實(shí)現(xiàn)應(yīng)該是這個(gè)create干的事,我們跟進(jìn)去看看源碼:
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall); } }); }
這里我們不用去看一些細(xì)節(jié),只去關(guān)注我們想知道的,這個(gè)Create方法到底干了什么,看到Proxy.newProxyInstance,哦!這就很明顯了,這里接口之所以能夠直接調(diào)用是使用了動(dòng)態(tài)代理技術(shù),產(chǎn)生了一個(gè)代理對(duì)象。binggo,一個(gè)問題解決!??!
什么時(shí)候開始將注解中參數(shù)拼裝成http請(qǐng)求的信息的?
動(dòng)態(tài)代理是干什么的?(大家最好單獨(dú)去學(xué)習(xí)下Java的動(dòng)態(tài)代理,有時(shí)候非常有用,資料網(wǎng)上大把大把的)
使用動(dòng)態(tài)代理一般是為了攔截方法,好在個(gè)方法執(zhí)行的時(shí)候,在執(zhí)行之前或之后干些自己的事情。這里回顧下Retrofit的使用,通過Create方法產(chǎn)生代理對(duì)象后直接就調(diào)用定義的方法了。那么,所有的小動(dòng)作必然是在調(diào)用接口方法的時(shí)候干的。具體就是invoke中的方法
if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall);
前兩個(gè)if判斷主要是為了跳過object和object中內(nèi)置的一些方法,除了這些,剩下的所有方法必然是我們?cè)诮涌谥卸x的,也就是我們要攔截的。那么真正干事的就是三句:
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.adapt(okHttpCall);
其中l(wèi)oadServiceMethod()方法就是用來拼裝http請(qǐng)求信息的,這個(gè)問題解決!
如何產(chǎn)生發(fā)起http請(qǐng)求對(duì)象的?
經(jīng)過上面的分析, OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);這就很明顯就是用來生產(chǎn)這個(gè)對(duì)象的。
如何將對(duì)象轉(zhuǎn)換成我們?cè)诮涌谥兄付ǖ姆祷刂档模?/p>
這里要說明下,之前我們定義接口的時(shí)候是這樣的:
@GET("hello/world") Call<News> getNews(@Query("num") String num,@Query("page")String page);
這里的Call和Okhttp的Call其實(shí)很像,其實(shí)連方法都幾乎一樣,但是Retrofit和Rxjava一起用的時(shí)候又可以這樣定義
@GET("book/search") Observable<Book> getSearchBook(@Query("q") String name, @Query("tag") String tag, @Query("start") int start, @Query("count") int count);
這里指定的返回值不再是Call,而是變成了RxJava的Observable,所以這里肯定有一步轉(zhuǎn)換,能將之前拼裝的信息轉(zhuǎn)換成我們指定的對(duì)象,具體核心就是最后一行的serviceMethod.adapt()干的。
總結(jié)
經(jīng)過以上分析,我們對(duì)Retrofit大致是干什么的很清楚了,主要就是拼裝http信息,并轉(zhuǎn)換成我們要的能夠發(fā)起http請(qǐng)求的對(duì)象。本文只是一個(gè)初探,后面我們將對(duì)每一步進(jìn)行深入分析。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
輕松實(shí)現(xiàn)Android3D效果通俗易懂
前幾天有粉絲要求計(jì)蒙寫一個(gè)3d效果的簡單教程,其實(shí)這個(gè)在Android官方demo中是有的,可能對(duì)于新手而言看不太明白,于是根據(jù)本人自己的理解來寫一個(gè)教程,并改成粉絲要求的樣子2021-08-08Android開發(fā)實(shí)現(xiàn)Fragment監(jiān)聽返回鍵事件功能的方法
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)Fragment監(jiān)聽返回鍵事件功能的方法,結(jié)合實(shí)例形式分析了Android使用Fragment監(jiān)聽并屏蔽返回鍵按鈕的實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下2017-11-11android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例
下面小編就為大家分享一篇android 獲取APP的唯一標(biāo)識(shí)applicationId的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02Android?Fragment實(shí)現(xiàn)頂部、底部導(dǎo)航欄
這篇文章主要為大家詳細(xì)介紹了Android?Fragment實(shí)現(xiàn)頂部、底部導(dǎo)航欄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09Android開發(fā)中使用顏色矩陣改變圖片顏色,透明度及亮度的方法
這篇文章主要介紹了Android開發(fā)中使用顏色矩陣改變圖片顏色,透明度及亮度的方法,涉及Android針對(duì)圖片的讀取、運(yùn)算、設(shè)置等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Android 百度地圖定位實(shí)現(xiàn)仿釘釘簽到打卡功能的完整代碼
這篇文章主要介紹了Android 百度地圖定位實(shí)現(xiàn)仿釘釘簽到打卡功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04Android中的Dalvik和ART詳解及區(qū)別分析
小編通過這篇文章給大家整理了什么是Dalvik和ART,并進(jìn)行了區(qū)別的分析,下面一起來看看。2016-07-07Android實(shí)現(xiàn)動(dòng)畫效果的自定義下拉菜單功能
這篇文章主要介紹了Android實(shí)現(xiàn)動(dòng)畫效果的自定義下拉菜單功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02