激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Retrofit之OKHttpCall源碼分析

Retrofit之OKHttpCall源碼分析

2022-02-19 16:38低情商的大仙 Android

這篇文章主要介紹了Retrofit之OKHttpCall源碼分析,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

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

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

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

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
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接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的一種包裝,下面我們詳細探究下每種方法,看是如何分別調用OkHttp的call中的方法的,有沒有做什么特殊處理。

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

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

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

request()方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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已經被實例化了直接調用它的request()方法,如果沒有的話,會調用createRawCall()方法先實例化,然后再調用request方法。

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

?
1
2
3
4
5
6
7
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方法來生成的,這里提供了參數而已,繼續跟進去:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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());
 }

這個方法最終其實就是調用okhttp3.Call中的這個方法:

?
1
2
3
public interface Factory {
 Call newCall(Request var1);
}

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

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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對象是延遲加載的,就是說在調用request方法時,其他的方法中有可能已經調用過createRawCall()方法,并由于某種原因失敗了,我們將這個失敗的異常記錄下來,在調用createRawCall()方法之前做一次判斷,如果已有異常就不需要調用createRawCall()方法了,提高了效率。

enque()

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
  }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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;
}
}

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

日常偷懶環節

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.jianshu.com/p/5cbdafae1a62

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 特一级黄色毛片 | av在线直播观看 | 久久国语对白 | 青青草免费观看 | 久久精品国产99国产精品亚洲 | 一级视频片 | 久久久久久麻豆 | 国产乱淫a∨片免费观看 | 久久精品视频免费 | 久久精品国产精品亚洲 | 麻豆19禁国产青草精品 | 亚洲成人精品久久久 | 亚洲综合精品 | 又黄又爽又色无遮挡免费 | 久草干 | 久久久久国 | 欧美a级在线免费观看 | xxxx hd videos| 香蕉成人在线视频 | 天堂福利电影 | 精品黑人一区二区三区国语馆 | 国模论坛 | 色七七久久影院 | 国内xxxx乱子另类 | 黄色网址免费在线 | 日日草视频 | 日本a大片 | 国产精品亚洲一区二区三区久久 | 国产精品午夜在线 | 成年人国产视频 | 成人精品aaaa网站 | 国产99久久久国产精品下药 | 成人在线免费小视频 | videos韩国| 日本精品黄色 | 欧美精品一区二区三区久久久 | 欧美大穴 | 9999久久| 国产午夜免费视频 | 免费在线国产 | 免费黄色欧美视频 |