從Retrofit的源碼來看 HTTP

關於Retrofit是啥,這裏就很少解釋了,仍是先來瞅下官網:java

而此次主要是瞭解它的底層動做機制,而在瞭解底層以前先來回顧一下官網的總體使用步驟:git

我們也以官網的這個例子爲例,先從簡單的使用開始逐步深刻,先新建一個工程:github

而後增長retrofit的build引用 ,以下:json

而後按官網的步驟,首先建立一個API接口,以下:api

我們以獲取用戶在github中的倉庫爲例,定義接口的API方法以下:數組

而後具體來調用一下,也如官網的描述同樣:瀏覽器

而後此時並未發起HTTP請求,須要像okhttp那樣調用一下這個方法,分同步和異步,固然這裏得用異步嘍,以下:緩存

而後增長訪問網絡的權限:網絡

先來查看一下個人github的倉庫:異步

而後運行一下:

成功了,其實這個接口返回的格式就是json,用瀏覽器能夠訪問看下結果:

接下來我們將結果打印成咱們看到的JSON格式同樣,而我們目前成功返回的是一個RsponseBody對像,它是來自okhttp的,以下:

此時就須要註冊一個轉換器了,這裏不細講怎麼用的,直接上結果,重點是經過簡單的使用掌握其深層次的本質原理,也就是源碼分析,下面來看怎麼作這個轉換:

那此時怎麼寫這個轉換工廠呢,這時須要再加一個庫,也就是gson的支持,關於gson是啥就裏就很少說了,直接添加依賴以下:

此時就能夠這麼用了:

接下來則須要修改API接口了,由於咱們不想看到返回的ResonseBody對象,而想看到具體的JSON,從網站上返回的JSON能夠看出其實就是一個JSON數組,因此返回的內容應該是一個List,因此修改一下:

而後裏面的每一個對象則須要咱們手動定義出來,先假設這個對像類爲Repo,以下:

接下來則定義該類:

而後再定義裏面的字段,這裏能夠經過JSON自動轉成Java的字段,能夠用JsonFormat工具,以下:

而後將Json數組中的對象內容拷至其中:

接下來我們來修改一下返回值,以下:

而後運行:

ok,對於retrofit的簡單用法就到此結束,重點是接下來分析它的源碼:先從使用入口來進行分析的突破口,而使用入口就是它:

能把它分析明白了,那對於retrofit的核心原理也就清楚啦,因此點進去看下它的源碼:

那此時就得看調用這個方法的對象是哪一個了,以下:

而這個API是我們定義的接口,也是抽象的。。

那此時就再得往前追溯了,得看它具體的對象:

若是知道了gitHubService的具體對象那麼最終咱們就能夠分析enqueue的具體實現了,因此定位其實現瞅一下:

這個方法是retrofit的核心,其實能夠看到有動態代理的東東,因此如今就集中來分析一下該實現:

從字面意思來看是驗證服務接口,看下究竟看了啥:

不重要,繼續往下讀:

這裏是一個配置項的檢查,表示是否要進行激進化的方法檢查,具體就不細看了,不是核心,主要是對咱們寫的api的方法合法性的檢查,如:

若是開啓了則會GitHubService一建立就會把全部的驗證都作完了,很利於咱們的調試,很早就能夠發現代碼寫得不對,可是!!不利於性能,大體知道就好了,繼續往下看:

動態代理嘛,難道說retrofit的核心機制就是動態代理?其實確實是它,不過目前還不得而知,關於動態代理是啥這裏就不過多解釋了,j2se的基礎,這裏用僞代碼來揭露其動態代碼的本質,首先看第二個參數:

其實動態代理就是首先生成一個實現了該接口的對象,僞代碼表示一下:

而後動態代理不是還有第三個參數InvocationHandler麼?以下:

其實它就會傳到動態生成的代理對象裏面,而後在每一個具體方法實現中則會用到它來生成,僞代碼以下:

若是說咱們在API接口中定義了多個方法,則在這個動態生成的對象中的實現也都是用invocationHandler來實現的,這就是動態代理的本質。 

那接下來就把精力花在這個invoke方法的具體實現上了,只要分析清楚了它,那麼就知道爲啥咱們僅僅聲明一個API接口retrofit就能夠實現一個網絡請求了,因此,研究一下invoke方法的具體實現:

而若是調用的是接口中的默認實現方法【這是Java8纔有的】,直接也不作其它任何處理了,對於使用retrofit而言不可能有這種默認方法,因此能夠略過這個判斷細節,繼續往下探究:

好暈呀,這三行中涉及到徹底陌生的ServiceMethod、OkHttpCall,徹底不明白,這裏就涉及到一個讀源碼的小技巧了,對於都看不懂的狀況下,先對涉及到的類都大至認識一下既可,不用深究,因此我們一個個先來大體瞅一下:

啥意思?首先得理解一下什麼是adapter,這個在咱們listview的開發中必用的概念,仍是先看一下它詞的本義:

也就是作轉接用的,也就是能夠猜想ServiceMethod的做用是:

而後此類的代碼量太大,也無法繼續往下看了,仍是返回到主調代碼處繼續瞭解其它的東東,繼續看下它:

而後我們來看一下ServiceMethod是如何生成的,經過生成細節看是否能進對ServiceMethod有一個進一步的瞭解,以下:

而後再看一下build()方法的細節:

而後再經過構造來實例化:

很經典的Builder模式,不過整個構建對象的細節徹底看不懂,先暫且放着,等回過頭按需再來查看,先來講一下Builder模式,人人皆知,這裏簡單說一下它的好處,一般咱們用Builder模式一般會這樣寫:

那它有啥好處呢?對於Person中有字段是有初始化成本的,什麼意思?好比咱們用正常的方式來初始化會這樣寫:

首先就在內存中有person對象了,接着再來修改一下性別:

而默認性別是女的,此句執行以後就須要在內存中將女姓給擦掉,而後用這個設置的男性來替代,這是有性能損耗的,接着再來修改年齡:

若是默認年齡是24,那此時內存中又得將24給擦掉而後再畫一個31歲的人,再接下來:

默認人是走路的行爲,此時又得內存進行擦除改掉用戶的行爲,因此說這種傳統的方式是有性能損耗的,而Builder模式則在構建對象時沒有提早生成內存,先生成一個配置清單,最終一塊兒來構建對象,這是它的最大好處之一,另一個好處就是當參數較多的時候這樣寫層次也比較清晰,關於builder模式這裏簡單提一下,還得回到我們所關心的retrofit實現原理上來:

打開瞅下它是啥?

那不就是說:

因此此時我們能夠看一下enqueue的具體實現:

先跳出這個實現細節,總的來回顧一下:

因此點進入再看最後一行的細節:

沒辦法,還得硬着頭皮點進去瞅下:

那看不懂呀,怎麼整,目前咱們要了解的這三行代碼,前兩行大體猜到了一些意思,而最後一行徹底不曉得其內部的細節,那接下來就從頭來細看一下,看是否經過細看能發現一些線索:

這個以前稍加看過,裏在就是維護了一個緩存,不過這裏仍是要看一下ServiceMethod的建立過程:

其中第一句看到了一個以前的疑問:

其中這上callAdapter是一個接口,因此此時不就解惑了麼,因此看一下callAdapter是如何建立的?

跟進去:

再往下跟:

接下來就得看一下這段代碼的實現了,先來瞅一下callAdapterFactories對象:

因此看一下它的調用,其實就是在build()方法中,如咱們在Activity寫的:

因此此時再看一下callAdapterFactories的建立來源:

而後就得看下一句了:

因此。。得看一下"platform.defaultCallAdapterFactory(callbackExecutor)"的細節:

如:

其中咱們能夠看到其實現中用到了一個「callbackExecutor」,經過它的執行而後再處理的回調:

因此得看一下callBackExecutor是如何傳遞進來的,此時就又得回調Retrofit.build()方法來了:

而後再進一步跟一下此callbackExecutor的建立細節:

那。。原來咱們看到的calladapter的做用是進行線程的轉換哦,那咱們繼續回到ServiceMethod.build()方法分析:

拿咱們定義的API接口方法來講就是指的:

接下來往下:

繼續往下

這不就是指麼:

好,再繼續往下:

另外有一個細節須要注意retrofit會對咱們寫的註解的正確性作驗證,會讓咱們更加規範的使用okhttp,好比multipart須要配合part來使用等,好對於ServiceMethod的build()方法能夠發現其實就是對咱們定義的API方法進行了解析並存下來,而後再實例化它:

至此,我們要想分析關鍵的第一句代碼就完全搞清楚其做用了,回顧一下:

好,接下來再分析核心的第二句代碼,好比好理解:

接着再來看第三句代碼,其實經過上面的分析也曉得其做用了,挼一下:

而後此時得回顧一下callAdpater是如何建立出來的:

而後此方法的調用是在調用build()時進行的,以下:

因此最終調用adapt()方法的實際上是ExecutorCallAdapterFactory裏面的了,以下:

也就是最終retrofit動態生成的對像在調用它裏面的getRepos()方法返回的是ExecutorCallbackCall對像,以下:

因此接下來咱們再來分析最初咱們分析不動的方法就順其天然啦,也就是:

那就是直接調用ExecutorCallbackCall.enqueue()方法,以下:

而代理的call是在咱們代理對像方法執行時動態建立的,以下:

因此最終就會轉到OkHttpCall.enqueue()方法來,以下:

其中仍是利用了ServiceMethod來對以前解析的東東來轉換成了okhttp的call,以下:

而後再利用Okhttp的Call進行異步請求,以下:

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }

    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) { t.printStackTrace(); } } });
  }

其中回調是先對OkHttp的Response進行解析,解析成Retrofit的Response:

而後這個parseResponse()方法就能夠體現出它與http的關係了,就是用了http的知識來編寫的,大體瞅一下:

其中從okhttp的reponse轉成retrofit的response最終還用到了converter了,以下:

最後還有一個知識就是retrofit如何集成rxjava,首先得集成一下rxjava,以下:

而後此時須要在增長一個calladapter,以下:

此時咱們的API定義返回就不用返回Call對像了,而是能夠返回一個Observable,以下:

而後就能夠用rxjava的那一套來進行接口請求及返回處理了,Retrofit是能夠支持多個Adapter的,瞅一下:

其中咱們知道Retrofit默認的Adapter爲CallAdapter,是能夠將ResponseBody轉換成一個Call對象,以下:

其具體實現是:

並達到一個切換線程的做用。

而此時加了一個Rxjava的CallAdapter,以下:

因此咱們在api能夠爲:

到此!!已經完整將Retrofit的整個核心機制分析完了,對於以後在實際工做中用Retrofit也更加踏實了~~說實話仍是挺複雜的。

相關文章
相關標籤/搜索