Retrofit源碼學(xué)習(xí)隨筆(1)

Retrofit

Retrofit用到了動態(tài)代理;

1.在 retrofit.create(interface service).service是用戶自定義的一個請求數(shù)據(jù)Url相關(guān)的接口(一下同稱自定義請求數(shù)據(jù)接口),這個接口不能繼承其他的接口拒啰。原因如下:
1.進(jìn)入create方法可以看到第一行的代碼如下:

 public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);

這個方法是檢測當(dāng)前這個類是否符合要求。跳進(jìn)去屎媳,代碼如下:

static <T> void validateServiceInterface(Class<T> service) {
    if (!service.isInterface()) {
      throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
    // Android (http://b.android.com/58753) but it forces composition of API declarations which is
    // the recommended pattern.
    if (service.getInterfaces().length > 0) {
      throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
  }

由第一個if語句可知這個類一定要是一個interface绳姨,第二個if語句則限定這個接口不能實現(xiàn)其他的接口佃牛。

1.Converter.Factory 用于創(chuàng)建轉(zhuǎn)換ResponseBody的Converter的工廠類
Converter<T, V> 用于將T轉(zhuǎn)換為V 的轉(zhuǎn)換類荣堰。convert方法定義了轉(zhuǎn)換的邏輯以及返回了最終轉(zhuǎn)換的結(jié)果床未。

最基本的用法

1.  Retrofit retrofit = new Retrofit.Builder()
2.              .baseUrl("http://www.baidu.com/")
3.              .addConverterFactory(EpgHomePageConverterFactory.create())
4. //               .addConverterFactory(GsonConverterFactory.create())
5.              .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
6.              .build(); //返回一個Retrofit的對象
7.   GetResponseService responseService = retrofit.create(GetResponseService.class);
8.  //通過動態(tài)代理獲取調(diào)用對應(yīng)的方法,結(jié)合CallAdapter以及ConverterFactory獲取對應(yīng)的Call對象持隧。       
     Call<EpgHomePage> responseBodyCall =    responseService.getResponse("xxxxx","xxxxx",1);
9. //調(diào)用Call的異步網(wǎng)絡(luò)請求即硼,請求網(wǎng)絡(luò)并獲取結(jié)果。   
     responseBodyCall.enqueue(new Callback<EpgHomePage>() {
            @Override
            public void onResponse(Response<EpgHomePage> response, Retrofit retrofit) {
                Log.i(TAG, response.body().getAreaUrl() + "areaUrl");

            }
            @Override
            public void onFailure(Throwable t) {

            }
        });

跟蹤上面代碼屡拨,探究Retrofit的工作流程:

  1. new Retrofit.Builder() 給Retrofit添加一個默認(rèn)的轉(zhuǎn)換工廠,用于轉(zhuǎn)換網(wǎng)絡(luò)請求的結(jié)果或者生成網(wǎng)絡(luò)請求對象褥实。
  public Builder() {
    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
  }
  1. baseUrl("http:/www.baidu.com/xxxx/xxxx/") 根據(jù)字符串參數(shù)呀狼,生成對應(yīng)的HttpUrl。

3.第七句损离,調(diào)用retrofit.create(GetResponseService.class)方法通過動態(tài)代理模式生成一個GetResponseService的實例哥艇。
4 . loadMethodHandler(method) 用于加載interface中的方法,并構(gòu)建對應(yīng)的網(wǎng)絡(luò)請求url僻澎,Converter以及CallAdapter貌踏。

static MethodHandler<?> create(Retrofit retrofit, Method method) {
    CallAdapter<Object> callAdapter = (CallAdapter<Object>) createCallAdapter(method, retrofit);
    Type responseType = callAdapter.responseType();
    Converter<ResponseBody, Object> responseConverter =
        (Converter<ResponseBody, Object>) createResponseConverter(method, retrofit, responseType);
    //根據(jù)方法的返回類型以及注釋十饥,構(gòu)成RequestFactory,繼而得到Request對象祖乳。
    RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
    return new MethodHandler<>(retrofit, requestFactory, callAdapter, responseConverter);
  }

5 . invoke(arg) 傳入方法的參數(shù)逗堵,然后通過OkHttp結(jié)合requestFactory和arg調(diào)用網(wǎng)絡(luò)請求,并且通過responseConverter將請求的結(jié)果轉(zhuǎn)換成對應(yīng)的T.

  Object invoke(Object... args) {
  return callAdapter.adapt(new OkHttpCall<>(retrofit, requestFactory, responseConverter, args));
}

6.請求網(wǎng)絡(luò)的操作在第九句眷昆,Call<EpgHomePage> 其實是OkHttpCall對象蜒秤,讓我們來看看enqueue方法。

  @Override public void enqueue(final Callback<T> callback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed");
      executed = true;
    }

    com.squareup.okhttp.Call rawCall;
    try {
    //獲取一個Call對象 用于網(wǎng)絡(luò)請求
      rawCall = createRawCall();
    } catch (Throwable t) {
      callback.onFailure(t);
      return;
    }
    if (canceled) {
      rawCall.cancel();
    }
    this.rawCall = rawCall;
    //執(zhí)行網(wǎng)絡(luò)請求亚斋,傳入一個Okhttp的CallBack作媚,回調(diào)函數(shù)中調(diào)用callback的方法,返回對應(yīng)請求狀態(tài).
    rawCall.enqueue(new com.squareup.okhttp.Callback() {
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(response, retrofit);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(Request request, IOException e) {
        callFailure(e);
      }
      //關(guān)鍵 將請求結(jié)果轉(zhuǎn)換成T類型帅刊。
      @Override public void onResponse(com.squareup.okhttp.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
    });
  }

  • response = parseResponse(rawResponse);
    跳進(jìn)parseResponse(rawResponse)看看是如何實現(xiàn)轉(zhuǎn)換的纸泡。代碼如下:
  private Response<T> parseResponse(com.squareup.okhttp.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    //請求失敗
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.readBodyToBytesIfNecessary(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        closeQuietly(rawBody);
      }
    }
    //請求成功
    if (code == 204 || code == 205) {
      return Response.success(null, rawResponse);
    }
  
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
       //通過調(diào)用ResponseCoverter的converter方法轉(zhuǎn)換。
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

通過以上源碼跟蹤分析:

總結(jié)

  • Converter.Factory<F,T>用于在最后請求的時候赖瞒,將請求得到的結(jié)果轉(zhuǎn)換成F之后再轉(zhuǎn)換成泛型T.
  • CallAdapter.Factory 決定了網(wǎng)絡(luò)請求Call適配器弟灼,底層依然是OkhttpCall,不過可以通過自定義Call在OkHttpCall的基礎(chǔ)上進(jìn)行拓展冒黑。對應(yīng)于自定一點的service的接口返回值類型田绑。
    Converter.Factory的方法返回CallAdapter<T>。
    CallAdapter<T>的Get方法返回 自定義的service的返回值類型抡爹,改類型封裝了Call對象掩驱。responseType 對應(yīng)于函數(shù)的返回值類型。
  • 默認(rèn)會設(shè)置HttpClient冬竟,也可以自己設(shè)置HttpClient欧穴。
  • 采用動態(tài)代理模式完成請求接口的實例化以及函數(shù)調(diào)用。
  • 但是在Retrofit2.0 泵殴,不管請求是否可以轉(zhuǎn)換成功涮帘,onResponse都會被調(diào)用,但是請求返回的結(jié)果不能轉(zhuǎn)換成相應(yīng)的對象笑诅,'response.body()'將返回null调缨,所以記住要處理情況!_耗恪O乙丁!

邊學(xué)習(xí)源碼妇多,邊做的一些隨筆伤哺,寫下來之后有其他理解在不斷完善,有不對的多多指教,歡迎交流立莉。O(∩_∩)O哈绢彤!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蜓耻,隨后出現(xiàn)的幾起案子茫舶,更是在濱河造成了極大的恐慌,老刑警劉巖媒熊,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奇适,死亡現(xiàn)場離奇詭異,居然都是意外死亡芦鳍,警方通過查閱死者的電腦和手機(jī)嚷往,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柠衅,“玉大人皮仁,你說我怎么就攤上這事》蒲纾” “怎么了贷祈?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喝峦。 經(jīng)常有香客問我势誊,道長,這世上最難降的妖魔是什么谣蠢? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任粟耻,我火速辦了婚禮,結(jié)果婚禮上眉踱,老公的妹妹穿的比我還像新娘挤忙。我一直安慰自己,他們只是感情好谈喳,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布册烈。 她就那樣靜靜地躺著,像睡著了一般婿禽。 火紅的嫁衣襯著肌膚如雪赏僧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天谈宛,我揣著相機(jī)與錄音次哈,去河邊找鬼。 笑死吆录,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的琼牧。 我是一名探鬼主播恢筝,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼哀卫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撬槽?” 一聲冷哼從身側(cè)響起此改,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侄柔,沒想到半個月后共啃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡暂题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年移剪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薪者。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡纵苛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出言津,到底是詐尸還是另有隱情攻人,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布悬槽,位于F島的核電站怀吻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏初婆。R本人自食惡果不足惜蓬坡,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烟逊。 院中可真熱鬧渣窜,春花似錦、人聲如沸宪躯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽访雪。三九已至详瑞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臣缀,已是汗流浹背坝橡。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留精置,地道東北人计寇。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親番宁。 傳聞我的和親對象是個殘疾皇子元莫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

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