Retrofit之OKHttpCall源碼分析
之前在Retrofit源碼初探一文中我們提出了三個(gè)問題:
- 什么時(shí)候開始將注解中參數(shù)拼裝成http請(qǐng)求的信息的?
- 如何產(chǎn)生發(fā)起http請(qǐng)求對(duì)象的?
- 如何將對(duì)象轉(zhuǎn)換成我們?cè)诮涌谥兄付ǖ姆祷刂档模?/li>
其中第一個(gè)問題前幾篇文章已經(jīng)做了解答,今天我們探究下第二個(gè)問題。
之前也分析過,具體生成這個(gè)請(qǐng)求對(duì)象的是這句代碼:
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
代碼很簡(jiǎn)單,那我們就來探究下這個(gè)OkHttpCall能干什么:
final class OkHttpCall<T> implements Call<T> {
可以看到其實(shí)主要實(shí)現(xiàn)了一個(gè)接口,所以我們看下這個(gè)接口都有哪些方法:
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(); }
看到這幾個(gè)方法有沒有很熟悉,沒錯(cuò),幾乎和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); } }
從這里我們猜測(cè),Retrofit的OkHttpCall其實(shí)就是對(duì)OkHttp的call的一種包裝,下面我們?cè)敿?xì)探究下每種方法,看是如何分別調(diào)用OkHttp的call中的方法的,有沒有做什么特殊處理。
之前有提過看源碼之前要帶著問題去看,那么對(duì)于這個(gè)OkHttpCall我們想知道什么?之前提到過這是對(duì)OkHttp的okhttp3.Call的一個(gè)封裝,那么每個(gè)方法必然會(huì)調(diào)用到okhttp3.Call對(duì)應(yīng)的方法,所以我們提出兩個(gè)問題:
- 這個(gè)類中okhttp3.Call對(duì)象是怎么生成的?
- 調(diào)用okhttp3.Call中對(duì)應(yīng)的方法時(shí)有沒有做什么特殊操作?
這兩個(gè)問題在每個(gè)主要方法中都能得到答案。
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)被實(shí)例化了直接調(diào)用它的request()方法,如果沒有的話,會(huì)調(diào)用createRawCall()方法先實(shí)例化,然后再調(diào)用request方法。
所以想要解答okhttp3.Call是怎么生成的,就來看看這個(gè)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ù)跟進(jìn)去:
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()); }
這個(gè)方法最終其實(shí)就是調(diào)用okhttp3.Call中的這個(gè)方法:
public interface Factory { Call newCall(Request var1); }
至于怎么根據(jù)Request生成Call是OkHttp干的,在ServiceMethod中的toCall方法,我們要做的就是用已有信息生成一個(gè)OkHttp的Request來,如何生成這個(gè)Request?這里利用了一個(gè)RequesetBuilder。
第一:處理方法級(jí)別的注解的信息
利用httpMethod,baseUrl,relativeUrl等直接new了一個(gè)RequestBuilder出來,這些信息都是從方法級(jí)別的注解中解析出來的。
第二:處理參數(shù)級(jí)別的注解信息
之前在生成ServiceMethod對(duì)象時(shí),利用參數(shù)級(jí)別的注解生成了一個(gè)ParameterHandler數(shù)組,每個(gè)Handler都有一個(gè)apply方法,將參數(shù)信息設(shè)置到一個(gè)RequestBuilder中,這個(gè)apply方法就是在這里調(diào)用的。
經(jīng)過上面兩部,一個(gè)包含了http請(qǐng)求完整信息的RequesetBuilder就生成了,最后build下生成一個(gè)Request傳到newCall方法中,則一個(gè)okhttp3.Call對(duì)象就生成了。
整個(gè)request()方法分析完了,做的事很簡(jiǎn)單,有okhttp3.Call對(duì)象就直接調(diào)用它的request()方法,沒有就生成一個(gè)再調(diào)用,但大家注意到?jīng)]有,他的代碼設(shè)計(jì)安排很奇怪。如果是我來寫這個(gè)方法,我可能會(huì)這樣寫:
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); } } }
可以看到,和我自己的代碼相比,原代碼多了一個(gè)記錄createRawCall()的異常的成員變量,這是處于效率考慮。由于我們的okhtt3.Call對(duì)象是延遲加載的,就是說在調(diào)用request方法時(shí),其他的方法中有可能已經(jīng)調(diào)用過createRawCall()方法,并由于某種原因失敗了,我們將這個(gè)失敗的異常記錄下來,在調(diào)用createRawCall()方法之前做一次判斷,如果已有異常就不需要調(diào)用createRawCall()方法了,提高了效率。
enque()
整個(gè)enque()方法的核心必然是調(diào)用okhttp3.Call的enque方法,我們重點(diǎn)關(guān)注調(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)用之前其實(shí)沒做什么,和request()方法差不多,做了下提前判斷而已,所以這里可以直接看代碼,核心就是調(diào)用了parseResponse()方法將返回值轉(zhuǎn)成了Retrofit的Response對(duì)象,然后調(diào)用了callSuccess()而已,所以我們跟進(jìn)去:
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; } }
這里邏輯很簡(jiǎn)單,根據(jù)不同的http狀態(tài)碼返回對(duì)應(yīng)的Response對(duì)象,這里有一點(diǎn),當(dāng)狀態(tài)碼正常時(shí),這里會(huì)利用一個(gè)converter將Body對(duì)象轉(zhuǎn)成自己想要的,比如轉(zhuǎn)成json等,具體處理是在serviceMethod.toResponse()中進(jìn)行的。
日常偷懶環(huán)節(jié)
好了,關(guān)鍵時(shí)刻來了,分析了這兩個(gè)方法后,OkHttpCall中的主要方法應(yīng)該都講到了,剩下的一些方法基本和上面兩個(gè)差不多,大家對(duì)著來就行了!
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Okhttp、Retrofit進(jìn)度獲取的方法(一行代碼搞定)
- Android 封裝Okhttp+Retrofit+RxJava,外加攔截器實(shí)例
- okhttp3.4.1+retrofit2.1.0實(shí)現(xiàn)離線緩存的示例
- OKHttp3(支持Retrofit)的網(wǎng)絡(luò)數(shù)據(jù)緩存Interceptor攔截器的實(shí)現(xiàn)
- RxJava+Retrofit+OkHttp實(shí)現(xiàn)多文件下載之?dāng)帱c(diǎn)續(xù)傳
- RxJava+Retrofit+OkHttp實(shí)現(xiàn)文件上傳
- 深入淺出RxJava+Retrofit+OkHttp網(wǎng)絡(luò)請(qǐng)求
- 淺談RxJava+Retrofit+OkHttp 封裝使用
- Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南
- Retrofit和OkHttp如何實(shí)現(xiàn)Android網(wǎng)絡(luò)緩存
相關(guān)文章
Fiddler實(shí)現(xiàn)手機(jī)抓包之小白入門必看
這篇文章主要介紹了Fiddler實(shí)現(xiàn)手機(jī)抓包之小白入門必看篇,需要的朋友可以參考下2018-03-03Android設(shè)備與外接U盤實(shí)現(xiàn)數(shù)據(jù)讀取操作的示例
本篇文章主要介紹了Android設(shè)備與外接U盤實(shí)現(xiàn)數(shù)據(jù)讀取操作的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11android layout XML解析錯(cuò)誤的解決方法
從別的地方復(fù)制過來XML時(shí),layout預(yù)覽時(shí)提示解析錯(cuò)誤。2013-04-04RecyclerView嵌套R(shí)ecyclerView滑動(dòng)卡頓的解決方法
這篇文章主要為大家詳細(xì)介紹了RecyclerView嵌套R(shí)ecyclerView滑動(dòng)卡頓的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12android調(diào)用webservice接口獲取信息
這篇文章主要為大家詳細(xì)介紹了android調(diào)用webservice接口獲取信息,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android中阻止AlertDialog關(guān)閉實(shí)例代碼
這篇文章主要介紹了Android阻止AlertDialog關(guān)閉實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-03-03Android來電監(jiān)聽和去電監(jiān)聽實(shí)現(xiàn)代碼
本文是關(guān)于來點(diǎn)監(jiān)聽和去電監(jiān)聽展開問題,通過實(shí)例代碼講解,對(duì)android來電監(jiān)聽和去電監(jiān)聽的相關(guān)知識(shí)感興趣的朋友一起看看吧2017-06-06Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法詳解
這篇文章主要介紹了Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法,結(jié)合實(shí)例形式分析了Android錄音功能的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07