Android開(kāi)源框架-Retrofit源碼解析(一)

Retrofit作為主流的網(wǎng)絡(luò)請(qǐng)求框架掠手,內(nèi)部封裝OkHttp發(fā)起請(qǐng)求锅必,也是聲明式Http客戶(hù)端倒槐,使用接口 + 注解的方式在接口中編寫(xiě)請(qǐng)求方法同窘,前文鋪墊了這么多玄帕,接下來(lái)我們就正式進(jìn)入Retrofit的源碼之旅

網(wǎng)絡(luò)通信流程8步驟&7個(gè)關(guān)鍵成員變量

在解析retrofit的源碼之前,先回顧下retrofit網(wǎng)絡(luò)通信的八步驟想邦,有一個(gè)宏觀的認(rèn)知

網(wǎng)絡(luò)通信8步
  • 創(chuàng)建retrofit實(shí)例
  • 定義一個(gè)網(wǎng)絡(luò)請(qǐng)求接口并為接口中的方法添加注解
  • 通過(guò)動(dòng)態(tài)代理生成網(wǎng)絡(luò)請(qǐng)求對(duì)象裤纹,也就是解析網(wǎng)絡(luò)請(qǐng)求接口中的注解
  • 通過(guò)網(wǎng)絡(luò)請(qǐng)求適配器將網(wǎng)絡(luò)請(qǐng)求對(duì)象進(jìn)行平臺(tái)適配(包括Android,Java8,IOS等的)
  • 通過(guò)網(wǎng)絡(luò)請(qǐng)求執(zhí)行器發(fā)送網(wǎng)絡(luò)請(qǐng)求
  • 通過(guò)數(shù)據(jù)轉(zhuǎn)換器解析數(shù)據(jù)
  • 通過(guò)回調(diào)執(zhí)行器切換線程
  • 用戶(hù)在主線程處理返回結(jié)果

以上就是Retrofit進(jìn)行網(wǎng)絡(luò)通信的步驟,大致代碼如下所示丧没,相信之前大家已經(jīng)耳熟能詳了

 val retrofit = Retrofit.Builder()
            .baseUrl("https://www.baidu.com") //請(qǐng)求url地址
            .addConverterFactory(GsonConverterFactory.create()) //設(shè)置數(shù)據(jù)解析器
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //設(shè)置Rxjava
            .build()
        val myInterface = retrofit.create(MyInterface::class.java)

        val call = myInterface.getCall()
        call.execute()

        call.enqueue(object : Callback<List<MyResponse>>{
            override fun onResponse(
                call: Call<List<MyResponse>>,
                response: Response<List<MyResponse>>
            ) {

            }

            override fun onFailure(call: Call<List<MyResponse>>, t: Throwable) {

            }

        })

7個(gè)關(guān)鍵成員變量

接下來(lái)我們開(kāi)始看下Retrofit的源碼鹰椒,首先就可以看到它創(chuàng)建了7個(gè)變量,先來(lái)搞清楚這些成員變量的作用,對(duì)之后理解Retrofit通過(guò)動(dòng)態(tài)解析挺有幫助的

  private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> callAdapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;

serviceMethodCache
  • 它是一個(gè)Map對(duì)象呕童,這里是ConcurrentHashMap對(duì)象漆际,看下它的key值method,也就是http請(qǐng)求的方法夺饲,value值是ServiceMethod灿椅,看到這里有點(diǎn)懵,查閱資料后才知道它代表網(wǎng)絡(luò)請(qǐng)求接口中對(duì)方法進(jìn)行解析钞支,然后解析之后的對(duì)象就是ServiceMethod,相當(dāng)于和注解中的@Get,@Post是成對(duì)出現(xiàn)的操刀,一一對(duì)應(yīng)烁挟;
  • 它的作用主要是用于緩存,比如說(shuō)存儲(chǔ)網(wǎng)絡(luò)請(qǐng)求的配置骨坑,還有網(wǎng)絡(luò)請(qǐng)求的方法撼嗓,數(shù)據(jù)轉(zhuǎn)換器,網(wǎng)絡(luò)請(qǐng)求適配器等等
callFactory
  • 它是請(qǐng)求網(wǎng)絡(luò)OkHttp的工廠欢唾,由于retrofit默認(rèn)的工廠就是OkhttpClient且警,這個(gè)在之前解析Okhttp的時(shí)候已經(jīng)詳細(xì)提過(guò)了,這里就不贅述了礁遣,這個(gè)工廠的作用就是用于生成我們的OkhttpClient
baseUrl
  • 這個(gè)很簡(jiǎn)單了斑芜,就是網(wǎng)絡(luò)請(qǐng)求基礎(chǔ)地址url,有了基地址祟霍,那就有相對(duì)地址杏头,在接口當(dāng)中的相對(duì)地址拼接起來(lái)就是個(gè)完整的url地址
List<Converter.Factory>,List<CallAdapter.Factory>
  • 這兩個(gè)都是集合,前者是數(shù)據(jù)轉(zhuǎn)換器工廠的集合沸呐,數(shù) 據(jù)轉(zhuǎn)換器就是對(duì)我們請(qǐng)求之后得到的response進(jìn)行的轉(zhuǎn)換成能用java對(duì)象醇王,總的來(lái)說(shuō)它是用于放置數(shù)據(jù)轉(zhuǎn)換器的工廠;后者是網(wǎng)絡(luò)請(qǐng)求適配器工廠的集合,把Call對(duì)象轉(zhuǎn)換成其他類(lèi)型崭添,比如說(shuō)平臺(tái)想支持Rxjava的話寓娩,就可以轉(zhuǎn)換成Rxjava的Call對(duì)象,就是用于生產(chǎn)CallAdapter,這里簡(jiǎn)單說(shuō)下棘伴,后期需要詳細(xì)說(shuō)明
Executor
  • 這是用于執(zhí)行回調(diào)的寞埠,在Android平臺(tái)當(dāng)中,會(huì)看到Platform這個(gè)變量在Retrofit的Builder內(nèi)部類(lèi)中排嫌,默認(rèn)使用MainThreadExecutor主線程池畸裳;其實(shí)我們只要明白,在Retrofit中的網(wǎng)絡(luò)請(qǐng)求最終都是通過(guò)線程池將handler進(jìn)行調(diào)配的淳地,可以處理主線程與子線程的切換;在處理異步的網(wǎng)絡(luò)請(qǐng)求時(shí)就需要它了
validateEagerly
  • Boolean類(lèi)型怖糊,這是個(gè)標(biāo)志位,表示的是是否立即解析接口中的方法

Retrofit的Builder內(nèi)部類(lèi)

  1. Retrofit是通過(guò)建造者模式構(gòu)建出來(lái)的颇象,我們首先看它的Builder內(nèi)部類(lèi)伍伤,是一個(gè)靜態(tài)內(nèi)部類(lèi)
public static final class Builder {
    private final Platform platform;
    private @Nullable okhttp3.Call.Factory callFactory;
    private @Nullable HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    private @Nullable Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }
    ....

  • 可以看下幾個(gè)成員變量,Platform表示Retrofit適配的平臺(tái)遣钳,默認(rèn)情況都是使用Android平臺(tái)扰魂;callFactory表示的是請(qǐng)求網(wǎng)絡(luò)Okhttp的工廠,默認(rèn)情況下就是OkhttpClient; baseUrl表示網(wǎng)絡(luò)請(qǐng)求url地址蕴茴,需要注意的是這里是HttpUrl劝评,所以需要把String類(lèi)型的url轉(zhuǎn)換為HttpUrl類(lèi)型才能夠被Retrofit使用;convertFactories表示數(shù)據(jù)轉(zhuǎn)換器工廠的集合;adapterFactories表示網(wǎng)絡(luò)適配工廠的集合倦淀;callbackExecutor表示執(zhí)行異步回調(diào)的;validateEagerly表示是否需要立即解析接口中的方法蒋畜,這個(gè)標(biāo)志位用在動(dòng)態(tài)代理要解析定義的注解和方法中,會(huì)進(jìn)行判斷撞叽;
  • 回顧這些成員變量和前面介紹地7個(gè)成員變量大體基本是類(lèi)似地姻成,除了PlatForm是Retrofit所沒(méi)有的,所以說(shuō)我們也能看出構(gòu)建者模式就是通過(guò)Builder這個(gè)內(nèi)部類(lèi)進(jìn)行配置愿棋,通過(guò)這個(gè)配置就能把Retrofit當(dāng)中的成員變量初始化
  1. 接下來(lái)我們查看下Builder類(lèi)的無(wú)參構(gòu)造方法
    public Builder() {
      this(Platform.get());
    }

傳入的是Platform.get(),返回的就是適配平臺(tái)科展,我們可以繼續(xù)看下Platorm內(nèi)部做了什么

 private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android() 
        : new Platform(true);
  }

  private final boolean hasJava8Types;
  private final @Nullable Constructor<Lookup> lookupConstructor;

  Platform(boolean hasJava8Types) {
    this.hasJava8Types = hasJava8Types;

    Constructor<Lookup> lookupConstructor = null;
    if (hasJava8Types) {
      try {
        lookupConstructor = Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookupConstructor.setAccessible(true);
      } catch (NoClassDefFoundError ignored) {
      } catch (NoSuchMethodException ignored) {
      }
    }
    this.lookupConstructor = lookupConstructor;

可以看到Platform其實(shí)就是個(gè)單例,它的get方法最終都是調(diào)用findPlatform方法糠雨,根據(jù)不同的運(yùn)行平臺(tái)來(lái)提供不同的線程池才睹;我們看下findPlatform方法中做了什么操作,它其實(shí)就是返回虛擬器所在的那個(gè)平臺(tái),默認(rèn)是在Android平臺(tái)

我們看下這個(gè)Android類(lèi)的代碼

class Android extends Platform {
    ....
    @Override
    public Executor defaultCallbackExecutor() {
       
      return new MainThreadExecutor();
    }  
    
    
    
    static final class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());
 
      @Override
      public void execute(Runnable r) {
        //通過(guò)handler post到主線程
        handler.post(r);
      }
    }

  • 它這里會(huì)返回一個(gè)默認(rèn)的回調(diào)執(zhí)行器defaultCallbackExecutor甘邀,見(jiàn)名知義砂竖,簡(jiǎn)單來(lái)說(shuō),這個(gè)執(zhí)行器的作用就是用于切換線程鹃答,同時(shí)在主線程中執(zhí)行回調(diào)方法
  • 它會(huì)返回一個(gè)MainThreadExecutor(),這個(gè)在下面已經(jīng)初始化好了乎澄,內(nèi)部有個(gè)Handler調(diào)用的是Looper.getMainLooper()這個(gè)靜態(tài)方法,這個(gè)Executor已經(jīng)和主線程(UI線程)綁定了测摔,為了把返回值的處理切換到UI線程, 所以說(shuō)這也是它能夠在主線程回調(diào)方法的原因
  1. 我們回到Builder內(nèi)部類(lèi)當(dāng)中置济,接下來(lái)我們看下Builder的有參構(gòu)造方法
    Builder(Platform platform) {
      this.platform = platform;
    }
    ...

這里很簡(jiǎn)單解恰,看到就是將平臺(tái)對(duì)象進(jìn)行賦值給Builder內(nèi)部類(lèi)的成員變量platform,對(duì)于Builder我們?nèi)粘J褂弥型ǔJ褂每諈⑦@個(gè)this(Platform.get())設(shè)置retrofit使用平臺(tái)

以上就是對(duì)Builder的簡(jiǎn)單分析浙于, 總結(jié)以下护盈,它主要是做了以下工作

  • 配置平臺(tái)類(lèi)型的對(duì)象Platform,默認(rèn)使用Android
  • 配置了網(wǎng)絡(luò)適配和數(shù)據(jù)轉(zhuǎn)換的工廠
  • Executor執(zhí)行異步回調(diào)羞酗,這里只是默認(rèn)值的初始化腐宋,還沒(méi)有真正部署到Retrofit的成員變量中

總結(jié)

這部分主要介紹了Retrofit的builder構(gòu)建者模式以及builder內(nèi)部類(lèi)的成員變量的作用,接下來(lái)會(huì)繼續(xù)學(xué)習(xí)分析Retrofit的源碼

未完待續(xù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末檀轨,一起剝皮案震驚了整個(gè)濱河市胸竞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌参萄,老刑警劉巖卫枝,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異讹挎,居然都是意外死亡校赤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)筒溃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)马篮,“玉大人,你說(shuō)我怎么就攤上這事怜奖』氩猓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵烦周,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我怎顾,道長(zhǎng)读慎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任槐雾,我火速辦了婚禮夭委,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘募强。我一直安慰自己株灸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布擎值。 她就那樣靜靜地躺著慌烧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸠儿。 梳的紋絲不亂的頭發(fā)上屹蚊,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天厕氨,我揣著相機(jī)與錄音,去河邊找鬼汹粤。 笑死命斧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘱兼。 我是一名探鬼主播国葬,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芹壕!你這毒婦竟也來(lái)了汇四?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哪雕,失蹤者是張志新(化名)和其女友劉穎船殉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體斯嚎,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡利虫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堡僻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糠惫。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钉疫,靈堂內(nèi)的尸體忽然破棺而出硼讽,到底是詐尸還是另有隱情,我是刑警寧澤牲阁,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布固阁,位于F島的核電站,受9級(jí)特大地震影響城菊,放射性物質(zhì)發(fā)生泄漏备燃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一凌唬、第九天 我趴在偏房一處隱蔽的房頂上張望并齐。 院中可真熱鬧,春花似錦客税、人聲如沸况褪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)测垛。三九已至,卻和暖如春秧均,著一層夾襖步出監(jiān)牢的瞬間赐纱,已是汗流浹背脊奋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留疙描,地道東北人诚隙。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像起胰,于是被迫代替她去往敵國(guó)和親久又。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容