Retrofit源碼解讀(五)–okhttpCall和adapt方法
標籤(空格分隔): Retrofit源碼 學習筆記
前言
- 以下的相關知識總結是通過慕課網的相關學習和自己的相關看法,如果有需要的可以去查看一下慕課網的相關教學,感覺還可以。
內容
上一節總結了ServiceMethod的類和相關作用,接下來就總結一下下面的兩行方法.
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
OkHttpCall(實現了okhttp的Call)
成員變量
//ServiceMethod對象 用來表述網絡請求參數信息
private final ServiceMethod<T, ?> serviceMethod;
//表述網絡接口參數
private final @Nullable Object[] args;
//該請求是否可以被取消 狀態標誌位
private volatile boolean canceled;
//okhttp3.Call 實際進行網絡請求的類
@GuardedBy("this")
private @Nullable okhttp3.Call rawCall;
//異常 出現異常的處理類
@GuardedBy("this")
private @Nullable Throwable creationFailure; // Either a RuntimeException or IOException.
//標誌位 用於異步方法執行邏輯使用
@GuardedBy("this")
private boolean executed;
構造函數
//ServiceMethod 請求接口參數
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
OkHttpCall就是Retrofit這個庫對於OkHttp的一個封裝,請求網絡的時候,不管事同步還是異步,都是通過OkHttpCall進行的請求
serviceMethod.callAdapter.adapt(okHttpCall);
通過調用adapt這個方法就是用來進行適配的,通過傳入創建好的okhttpcall來獲取他的返回值
我們可以看到callAdapter是屬於serviceMethod裏面的參數,這個參數的賦值在serviceMethod的build的時候進行的賦值。adapt也就是通過適配器模式,轉換成我們需要的一些請求,比如當我們addCallAdapterFactory(RxJavaCallAdapterFactory.create())的時候,我們想要的就是通過RxJava來處理請求返回的response,那麼通過adapt這個方法就可以轉換成Observable這個類型,來讓我們操作。也就是可以對於不同平臺的其他類型。
所以當通過crete這個方法之後,我們就創建了 TestInterface service = getRetrofit().create(TestInterface.class);
這個接口實例,然後通過接口實例的
service.getQiuShiJsonString()
但是有個疑問,那就是接口實例調用方法,這個時候我們可以想到的就是前面提到的動態代理,榮光Proxy.newProxyInstance進行攔截,然後調用InvocationHandler中的invoke()方法來進行實際的操作,最終我們會返回一個OkHttpcall類型的call對象來進行實際的網絡請求,所以說我們的service.getQiuShiJsonString()這個請求,就是通過動態代理返還給我們的call對象來進行網絡請你去,這個okhttpcall又是對okhttp的網絡請求的封裝,所以也就是通過okhttp來進行的同步和異步請求。
Retrofit請求
- 同步請求OkHttpCall.execute()
- 異步請求OkHttpCall.enqueue(callback)
經過之前的分析,我們可以知道Retrofit的Call其實是對於OkHttpCall的請求的封裝,因此Retrofit的同步和異步請求就是調用OkHttpCall的請求來進行的同步和異步
同步VS異步
- 回調執行器
- 整個的同步和異步請求比較類似,區別在於回調執行器,異步會把數據返回之後交給execute(回調執行器來做),由Executor來指定相應的線程去完成不同的操作
Retrofit同步請求
- 1、ParameterHandler進行請求方法的參數解析
- 2、根據ServiceMethod這個對象創建OkHttp的Request對象
- 有了這個request對象我們才能調用okhttp進行網絡請求
- 而且ServiceMethod包含了我們所有的網絡請求的基本信息(當前請求和已經換成的請求)
- 而且這個ServiceMethod有緩存,使用的是ConcurrentHashMap,進行緩存是爲了高效運行
- 3、通過okhttpCall發送網絡請求
- 4、調用converter進行數據解析(默認是GsonConverter)
qiuShiJson.execute(); //同步請求方法
@Override public Response<T> execute() throws IOException {
//創建okhttp3 裏面的Call對象 來請求數據
okhttp3.Call call;
//加鎖
synchronized (this) {
//是否執行的標誌位
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
if (call == null) {
try {
//創建一個新的okhttpcall
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
//當canceled爲true的時候 直接取消請求
if (canceled) {
call.cancel();
}
//接下來分析這個parseResponse()方法 因爲call.execute()這個call其實就是okhttp的call,然後這個請求就已經交給了okhttp進行請求,這裏暫不做相關分析
return parseResponse(call.execute());
}
//創建okhttp3.Call
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
//創建okhttp call
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
//直接返回
return call;
}
//上述方法就是把我們網絡請求中的參數,調用ParameterHandler對象來進行參數的解析,然後在通過serviceMethod.toRequest()方法來生成Request這個對象,最後把這個Request對象通過serviceMethod的CallFactory工廠的newCall()方法創建我們的實際請求的OkHttp.Call對象
parseResponse(call.execute())方法
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
//獲取到ResponseBody
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();
//返回碼 暫不解析。。。
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
//主要是這個toResponse()方法 最終也就是調用我們傳入的GsonConverter轉換器 轉換成我們需要的數據類型
/** Builds a method return value from an HTTP response body.
//R toResponse(ResponseBody body) throws IOException //{
// return responseConverter.convert(body);
//}
T body = serviceMethod.toResponse(catchingBody);
//返回成功的response 完成了整個的response解析
return Response.success(body, rawResponse);
}
}
上述就是retrofit的同步請求的相關解析。其實簡單來說,retrofit就是通過接口的註解和參數,把我們的http請求進行了包裝,然後呢我們在retrofit使用的時候,然後在使用的時候我們只需要把重點放在接口的創建上,通過接口來配置方法和參數,其他都是通過他的內部來進行邏輯處理,他內部也是通過動態代理將客戶端寫好的接口中的方法轉換成ServiceMethod對象,然後通過這個ServiceMethod對象來獲取到我們需要的信息(數據轉換器/網絡適配器等、、),最終網絡底層的請求還是交給了okhttp來進行。
Retrofit異步請求
異步請求的相關原理基本和Retrofit的同步請求類似,關於那4步驟可以看Retrofit的同步請求剛開始那4條。主要的區別就是在於異步的異步回調
@Override public void enqueue(final Callback<T> callback) {
//okhttpcall
okhttp3.Call call;
//錯誤
Throwable failure;
synchronized (this) {
//和同步類似
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
try {
//創建rawCall
call = rawCall = createRawCall();
} catch (Throwable 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)
throws IOException {
Response<T> response;
try {
//這個是正確請求返回數據只會 解析response
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
//調用成功的邏輯 回調執行器
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
//失敗的時候 回調執行器
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
//失敗的邏輯處理
private void callFailure(Throwable e) {
try {
//這個就是callback 在我們自己的實現的onFailure裏面進行異常的處理
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callSuccess(Response<T> response) {
try {
//成功的返回 這個會在我們異步請求中 複寫這個onResponse方法來進行 數據邏輯處理,第一個參數是OkHttpCall 第二個參數就是response這個 也就是如果我們接受的是HttpResult<List<TestBean>>>這個類型 那麼我們返回的也是這個類型 Response<HttpResult<List<TestBean>>> response 這個response就是我們的最終解析後的數據類型
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}