国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Retrofit之OKHttpCall源碼分析

2019-10-22 18:11:33
字體:
來源:轉載
供稿:網(wǎng)友

之前在Retrofit源碼初探一文中我們提出了三個問題:

  1. 什么時候開始將注解中參數(shù)拼裝成http請求的信息的?
  2. 如何產(chǎn)生發(fā)起http請求對象的?
  3. 如何將對象轉換成我們在接口中指定的返回值的?

其中第一個問題前幾篇文章已經(jīng)做了解答,今天我們探究下第二個問題。

之前也分析過,具體生成這個請求對象的是這句代碼:

OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);

代碼很簡單,那我們就來探究下這個OkHttpCall能干什么:

final class OkHttpCall<T> implements Call<T> {

可以看到其實主要實現(xiàn)了一個接口,所以我們看下這個接口都有哪些方法:

public interface Call<T> extends Cloneable { Response<T> execute() throws IOException; void enqueue(Callback<T> callback); boolean isExecuted(); void cancel(); boolean isCanceled(); Call<T> clone(); /** The original HTTP request. */ Request request();}

看到這幾個方法有沒有很熟悉,沒錯,幾乎和Okhttp的Call方法一模一樣,我們看下okhttp的call接口:

public interface Call extends Cloneable { Request request(); Response execute() throws IOException; void enqueue(Callback var1); void cancel(); boolean isExecuted(); boolean isCanceled(); Call clone(); public interface Factory {  Call newCall(Request var1); }}

從這里我們猜測,Retrofit的OkHttpCall其實就是對OkHttp的call的一種包裝,下面我們詳細探究下每種方法,看是如何分別調(diào)用OkHttp的call中的方法的,有沒有做什么特殊處理。

之前有提過看源碼之前要帶著問題去看,那么對于這個OkHttpCall我們想知道什么?之前提到過這是對OkHttp的okhttp3.Call的一個封裝,那么每個方法必然會調(diào)用到okhttp3.Call對應的方法,所以我們提出兩個問題:

  1. 這個類中okhttp3.Call對象是怎么生成的?
  2. 調(diào)用okhttp3.Call中對應的方法時有沒有做什么特殊操作?

這兩個問題在每個主要方法中都能得到答案。

request()方法

 @Override public synchronized Request request() { okhttp3.Call call = rawCall; if (call != null) {  return call.request(); } if (creationFailure != null) {  if (creationFailure instanceof IOException) {  throw new RuntimeException("Unable to create request.", creationFailure);  } else if (creationFailure instanceof RuntimeException) {  throw (RuntimeException) creationFailure;  } else {  throw (Error) creationFailure;  } } try {  return (rawCall = createRawCall()).request(); } catch (RuntimeException | Error e) {  throwIfFatal(e); // Do not assign a fatal error to creationFailure.  creationFailure = e;  throw e; } catch (IOException e) {  creationFailure = e;  throw new RuntimeException("Unable to create request.", e); } }

可以看到,大致邏輯就是如果okhttp3.Call已經(jīng)被實例化了直接調(diào)用它的request()方法,如果沒有的話,會調(diào)用createRawCall()方法先實例化,然后再調(diào)用request方法。

所以想要解答okhttp3.Call是怎么生成的,就來看看這個createRawCall()方法:

 private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = serviceMethod.toCall(args); if (call == null) {  throw new NullPointerException("Call.Factory returned null."); } return call; }

可以看到核心方法還是ServiceMethod中的toCall方法來生成的,這里提供了參數(shù)而已,繼續(xù)跟進去:

okhttp3.Call toCall(@Nullable Object... args) throws IOException { RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,  contentType, hasBody, isFormEncoded, isMultipart); @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types. ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) {  throw new IllegalArgumentException("Argument count (" + argumentCount   + ") doesn't match expected count (" + handlers.length + ")"); } for (int p = 0; p < argumentCount; p++) {  handlers[p].apply(requestBuilder, args[p]); } return callFactory.newCall(requestBuilder.build()); }

這個方法最終其實就是調(diào)用okhttp3.Call中的這個方法:

 public interface Factory {  Call newCall(Request var1); }

至于怎么根據(jù)Request生成Call是OkHttp干的,在ServiceMethod中的toCall方法,我們要做的就是用已有信息生成一個OkHttp的Request來,如何生成這個Request?這里利用了一個RequesetBuilder。

第一:處理方法級別的注解的信息

利用httpMethod,baseUrl,relativeUrl等直接new了一個RequestBuilder出來,這些信息都是從方法級別的注解中解析出來的。
第二:處理參數(shù)級別的注解信息

之前在生成ServiceMethod對象時,利用參數(shù)級別的注解生成了一個ParameterHandler數(shù)組,每個Handler都有一個apply方法,將參數(shù)信息設置到一個RequestBuilder中,這個apply方法就是在這里調(diào)用的。

經(jīng)過上面兩部,一個包含了http請求完整信息的RequesetBuilder就生成了,最后build下生成一個Request傳到newCall方法中,則一個okhttp3.Call對象就生成了。

整個request()方法分析完了,做的事很簡單,有okhttp3.Call對象就直接調(diào)用它的request()方法,沒有就生成一個再調(diào)用,但大家注意到?jīng)]有,他的代碼設計安排很奇怪。如果是我來寫這個方法,我可能會這樣寫:

 public synchronized Request request1() { okhttp3.Call call = rawCall; if (call != null) {  return call.request(); }else{  try {  return (rawCall = createRawCall()).request();  } catch (RuntimeException | Error e) {  throwIfFatal(e); // Do not assign a fatal error to creationFailure.  throw e;  } catch (IOException e) {  throw new RuntimeException("Unable to create request.", e);  } } }

可以看到,和我自己的代碼相比,原代碼多了一個記錄createRawCall()的異常的成員變量,這是處于效率考慮。由于我們的okhtt3.Call對象是延遲加載的,就是說在調(diào)用request方法時,其他的方法中有可能已經(jīng)調(diào)用過createRawCall()方法,并由于某種原因失敗了,我們將這個失敗的異常記錄下來,在調(diào)用createRawCall()方法之前做一次判斷,如果已有異常就不需要調(diào)用createRawCall()方法了,提高了效率。

enque()

整個enque()方法的核心必然是調(diào)用okhttp3.Call的enque方法,我們重點關注調(diào)用之前有做什么,調(diào)用之后做了什么:

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

在調(diào)用之前其實沒做什么,和request()方法差不多,做了下提前判斷而已,所以這里可以直接看代碼,核心就是調(diào)用了parseResponse()方法將返回值轉成了Retrofit的Response對象,然后調(diào)用了callSuccess()而已,所以我們跟進去:

 Response<T> parseResponse(okhttp3.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.buffer(rawBody);  return Response.error(bufferedBody, rawResponse);  } finally {  rawBody.close();  } } if (code == 204 || code == 205) {  rawBody.close();  return Response.success(null, rawResponse); } ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try {  T body = serviceMethod.toResponse(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; } }

這里邏輯很簡單,根據(jù)不同的http狀態(tài)碼返回對應的Response對象,這里有一點,當狀態(tài)碼正常時,這里會利用一個converter將Body對象轉成自己想要的,比如轉成json等,具體處理是在serviceMethod.toResponse()中進行的。

日常偷懶環(huán)節(jié)

好了,關鍵時刻來了,分析了這兩個方法后,OkHttpCall中的主要方法應該都講到了,剩下的一些方法基本和上面兩個差不多,大家對著來就行了!

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網(wǎng)。


注:相關教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 龙岩市| 青铜峡市| 乌恰县| 道真| 玉山县| 长丰县| 兴和县| 聂拉木县| 三台县| 塔河县| 颍上县| 华阴市| 梨树县| 额尔古纳市| 托克逊县| 镇巴县| 类乌齐县| 乐至县| 孟州市| 东海县| 白银市| 乃东县| 邹城市| 阿克苏市| 大石桥市| 广饶县| 瓮安县| 盘山县| 黎川县| 清流县| 夏河县| 墨竹工卡县| 古交市| 肥乡县| 克拉玛依市| 西充县| 新晃| 会昌县| 盐津县| 丘北县| 碌曲县|