Android OkHttp使用和源码详解
介紹
????????OkHttp 是一套處理 HTTP 網絡請求的依賴庫,由 Square 公司設計研發并開源,目前可以在 Java 和 Kotlin 中使用。對于 Android App 來說,OkHttp 現在幾乎已經占據了所有的網絡請求操作,RetroFit + OkHttp 實現網絡請求似乎成了一種標配。因此它也是每一個 Android 開發工程師的必備技能,了解其內部實現原理可以更好地進行功能擴展、封裝以及優化。
????????適用于 Android 和 Java 應用程序的 HTTP 和 HTTP/2 客戶端。
????????OkHttp的4.0.x版本已經全部由java替換到了Kotlin,API的一些使用也會有些不同。
????????Github傳送門
????????文檔和 API
要求
支持的版本
????????4.0.x :Android 5.0+(API 級別 21+)和 Java 8+。
????????3.12.x :Android 2.3+(API 級別 9+)和 Java 7+。平臺可能不支持 TLSv1.2。(2021-12-31不再支持)
????????OkHttp有一個庫的依賴Okio,用于高性能I/O一個小型library。它適用于 Okio 1.x(用 Java 實現)或 Okio 2.x(升級到 Kotlin)。
????????????????本文使用的OkHttp的版本為3.14.2,不是不會接入高版本,主要是4.0.x版本已經全部由java替換到了Kotlin,Kotlin不太熟怕理解錯了,誤導人民群眾。
dependencies?{//本文使用implementation?'com.squareup.okio:okio:1.15.0'implementation?'com.squareup.okhttp3:okhttp:3.14.2'//高版本//?define?a?BOM?and?its?versionimplementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.0"))//?define?any?required?OkHttp?artifacts?without?versionimplementation("com.squareup.okhttp3:okhttp")implementation("com.squareup.okhttp3:logging-interceptor") }網絡請求流程分析
????????OkHttp 經過幾次迭代后,已經發生了很多變化。更好的 WebSocket 支持、更多的 Interceptor 責任鏈,甚至連最核心的 HttpEngine 也變成了 HttpCodec。本文會重新梳理整個網絡請求的流程,以及實現機制。
????????先看下 OkHttp 的基本使用:
public?void?okHttp(String?url){//創建OkHttpClient對象OkHttpClient?client?=?new?OkHttpClient();//創建RequestRequest?request?=?new?Request.Builder().url(url).build();//創建Call對象client.newCall(request)//通過execute()方法獲得請求響應的Response對象client.newCall(request).enqueue(new?Callback()?{@Overridepublic?void?onFailure(Call?call,?IOException?e)?{}@Overridepublic?void?onResponse(Call?call,?Response?response)?throws?IOException?{if(response.isSuccessful()){String?result?=?response.body().string();//處理UI需要切換到UI線程處理}}});}????????除了直接 new OkHttpClient 之外,還可以使用內部工廠類 Builder 來設置 OkHttpClient。如下所示:
????public?void?buildHttp(String?url){OkHttpClient.Builder?builder?=?new?OkHttpClient.Builder();builder.connectTimeout(15,?TimeUnit.SECONDS)//設置超時.addInterceptor(interceptor)????//攔截器.proxy(proxy)???????//設置代理.cache(cache);??????//設置緩存Request?request?=?new?Request.Builder().url(url).build();builder.build().newCall(request).enqueue(new?Callback()?{@Overridepublic?void?onFailure(Call?call,?IOException?e)?{}@Overridepublic?void?onResponse(Call?call,?Response?response)?throws?IOException?{}});}????????請求操作的起點從 OkHttpClient.newCall().enqueue() 方法開始
OkHttpClient.newCall
??@Override?public?Call?newCall(Request?request)?{return?RealCall.newRealCall(this,?request,?false?/*?for?web?socket?*/);}????????RealCall.newRealCall.java?
static?RealCall?newRealCall(OkHttpClient?client,?Request?originalRequest,?boolean?forWebSocket)?{//?Safely?publish?the?Call?instance?to?the?EventListener.RealCall?call?=?new?RealCall(client,?originalRequest,?forWebSocket);call.transmitter?=?new?Transmitter(client,?call);return?call;}????????這個方法會返回一個 RealCall 對象,通過它將網絡請求操作添加到請求隊列中。
RealCall.enqueue
??@Override?public?void?enqueue(Callback?responseCallback)?{synchronized?(this)?{if?(executed)?throw?new?IllegalStateException("Already?Executed");executed?=?true;}transmitter.callStart();client.dispatcher().enqueue(new?AsyncCall(responseCallback));}????????client.dispatcher()返回Dispatcher,調用 Dispatcher 的 enqueue 方法,執行一個異步網絡請求的操作。
Dispatcher 是 OkHttpClient 的調度器,是一種門戶模式。主要用來實現執行、取消異步請求操作。本質上是內部維護了一個線程池去執行異步操作,并且在 Dispatcher 內部根據一定的策略,保證最大并發個數、同一 host 主機允許執行請求的線程個數等。
Dispatcher.enqueue
??void?enqueue(AsyncCall?call)?{synchronized?(this)?{readyAsyncCalls.add(call);if?(!call.get().forWebSocket)?{AsyncCall?existingCall?=?findExistingCallWithHost(call.host());if?(existingCall?!=?null)?call.reuseCallsPerHostFrom(existingCall);}}promoteAndExecute();}????????實際上就是使用線程池執行了一個 AsyncCall,而 AsyncCall 繼承了 NamedRunnable,NamedRunnable 實現了 Runnable 接口,因此整個操作會在一個子線程(非 UI 線程)中執行。
NamedRunnable
/***?Runnable?implementation?which?always?sets?its?thread?name.*/ public?abstract?class?NamedRunnable?implements?Runnable?{protected?final?String?name;public?NamedRunnable(String?format,?Object...?args)?{this.name?=?Util.format(format,?args);}@Override?public?final?void?run()?{String?oldName?=?Thread.currentThread().getName();Thread.currentThread().setName(name);try?{execute();}?finally?{Thread.currentThread().setName(oldName);}}protected?abstract?void?execute(); }????????在 run 方法中執行了 一個抽象方法 execute 這個抽象方法被 AsyncCall 實現。
AsyncCall.execute
@Override?protected?void?execute()?{boolean?signalledCallback?=?false;transmitter.timeoutEnter();try?{Response?response?=?getResponseWithInterceptorChain();signalledCallback?=?true;responseCallback.onResponse(RealCall.this,?response);}?catch?(IOException?e)?{if?(signalledCallback)?{//?Do?not?signal?the?callback?twice!Platform.get().log(INFO,?"Callback?failure?for?"?+?toLoggableString(),?e);}?else?{responseCallback.onFailure(RealCall.this,?e);}}?finally?{client.dispatcher().finished(this);}}????????從上面看出而真正獲取請求結果的方法是在 getResponseWithInterceptorChain 方法中,從名字也能看出其內部是一個攔截器的調用鏈。
RealCall.getResponseWithInterceptorChain
??Response?getResponseWithInterceptorChain()?throws?IOException?{//?Build?a?full?stack?of?interceptors.List<Interceptor>?interceptors?=?new?ArrayList<>();interceptors.addAll(client.interceptors());interceptors.add(new?RetryAndFollowUpInterceptor(client));interceptors.add(new?BridgeInterceptor(client.cookieJar()));interceptors.add(new?CacheInterceptor(client.internalCache()));interceptors.add(new?ConnectInterceptor(client));if?(!forWebSocket)?{interceptors.addAll(client.networkInterceptors());}interceptors.add(new?CallServerInterceptor(forWebSocket));Interceptor.Chain?chain?=?new?RealInterceptorChain(interceptors,?transmitter,?null,?0,originalRequest,?this,?client.connectTimeoutMillis(),client.readTimeoutMillis(),?client.writeTimeoutMillis());boolean?calledNoMoreExchanges?=?false;try?{Response?response?=?chain.proceed(originalRequest);if?(transmitter.isCanceled())?{closeQuietly(response);throw?new?IOException("Canceled");}return?response;}?catch?(IOException?e)?{calledNoMoreExchanges?=?true;throw?transmitter.noMoreExchanges(e);}?finally?{if?(!calledNoMoreExchanges)?{transmitter.noMoreExchanges(null);}}}????????Interceptor:攔截器是一種強大的機制,可以監視、重寫和重試調用。
每一個攔截器的作用如下:
-
BridgeInterceptor:主要對 Request 中的 Head 設置默認值,比如 Content-Type、Keep-Alive、Cookie 等。
-
CacheInterceptor:負責 HTTP 請求的緩存處理。
-
ConnectInterceptor:負責建立與服務器地址之間的連接,也就是 TCP 鏈接。
-
CallServerInterceptor:負責向服務器發送請求,并從服務器拿到遠端數據結果。
-
RetryAndFollowUpInterceptor:此攔截器從故障中恢復,并根據需要執行重定向。如果呼叫被取消,它可能會引發IOException。
在添加上述幾個攔截器之前,會調用 client.interceptors 將開發人員設置的攔截器添加到列表當中。
????????對于 Request 的 Head 以及 TCP 鏈接,我們能控制修改的成分不是很多。所以咱們了解 CacheInterceptor 和 CallServerInterceptor。
CacheInterceptor?緩存攔截器
????????CacheInterceptor 主要做以下幾件事情:
????????1、根據 Request 獲取當前已有緩存的 Response(有可能為 null),并根據獲取到的緩存 Response,創建 CacheStrategy 對象。
????????2、 通過 CacheStrategy 判斷當前緩存中的 Response 是否有效(比如是否過期),如果緩存 Response 可用則直接返回,否則調用 chain.proceed() 繼續執行下一個攔截器,也就是發送網絡請求從服務器獲取遠端 Response。
? ? ? ? ?3、如果從服務器端成功獲取 Response,再判斷是否將此 Response 進行緩存操作。
CacheInterceptor.intercept
@Override?public?Response?intercept(Chain?chain)?throws?IOException?{//根據?Request?獲取當前已有緩存的?Response(有可能為?null),并根據獲取到的緩存?ResponseResponse?cacheCandidate?=?cache?!=?null??cache.get(chain.request()):?null;//獲取當前時間long?now?=?System.currentTimeMillis();//創建?CacheStrategy?對象//通過?CacheStrategy?來判斷緩存是否有效CacheStrategy?strategy?=?new?CacheStrategy.Factory(now,?chain.request(),?cacheCandidate).get();Request?networkRequest?=?strategy.networkRequest;Response?cacheResponse?=?strategy.cacheResponse;if?(cache?!=?null)?{cache.trackResponse(strategy);}if?(cacheCandidate?!=?null?&&?cacheResponse?==?null)?{closeQuietly(cacheCandidate.body());?//?The?cache?candidate?wasn't?applicable.?Close?it.}//如果我們被禁止使用網絡,并且緩存不足,則失敗。返回空相應(Util.EMPTY_RESPONSE)if?(networkRequest?==?null?&&?cacheResponse?==?null)?{return?new?Response.Builder().request(chain.request()).protocol(Protocol.HTTP_1_1).code(504).message("Unsatisfiable?Request?(only-if-cached)").body(Util.EMPTY_RESPONSE).sentRequestAtMillis(-1L).receivedResponseAtMillis(System.currentTimeMillis()).build();}//?如果緩存有效,緩存?Response?可用則直接返回if?(networkRequest?==?null)?{return?cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}//沒有緩存或者緩存失敗,則發送網絡請求從服務器獲取ResponseResponse?networkResponse?=?null;try?{//執行下一個攔截器,networkRequest//發起網絡請求networkResponse?=?chain.proceed(networkRequest);}?finally?{//如果我們在I/O或其他方面崩潰,請不要泄漏cache?body。if?(networkResponse?==?null?&&?cacheCandidate?!=?null)?{closeQuietly(cacheCandidate.body());}}。。。//通過網絡獲取最新的ResponseResponse?response?=?networkResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).networkResponse(stripBody(networkResponse)).build();//如果開發人員有設置自定義cache,則將最新response緩存if?(cache?!=?null)?{if?(HttpHeaders.hasBody(response)?&&?CacheStrategy.isCacheable(response,?networkRequest))?{//?Offer?this?request?to?the?cache.CacheRequest?cacheRequest?=?cache.put(response);return?cacheWritingResponse(cacheRequest,?response);}//返回response(緩存或網絡)return?response;}通過 Cache 實現緩存功能
????????通過上面緩存攔截器的流程可以看出,OkHttp 只是規范了一套緩存策略,但是具體使用何種方式將數據緩存到本地,以及如何從本地緩存中取出數據,都是由開發人員自己定義并實現,并通過 OkHttpClient.Builder 的 cache 方法設置。
????????OkHttp 提供了一個默認的緩存類 Cache.java,我們可以在構建 OkHttpClient 時,直接使用 Cache 來實現緩存功能。只需要指定緩存的路徑,以及最大可用空間即可,如下所示:
OkHttpClient.Builder?builder?=?new?OkHttpClient.Builder();builder.connectTimeout(15,?TimeUnit.SECONDS)//設置超時攔截器.addInterceptor(new?Interceptor()?{@Overridepublic?Response?intercept(Chain?chain)?throws?IOException?{return?null;}})//設置代理.proxy(new?Proxy(Proxy.Type.HTTP,null))?//設置緩存//AppGlobalUtils.getApplication()?通過反射得到Application實例//getCacheDir內置?cache?目錄作為緩存路徑//maxSize?10*1024*1024?設置最大緩存10MB.cache(new?Cache(AppGlobalUtils.getApplication().getCacheDir(),10*1024*1024));Cache 內部使用了 DiskLruCach 來實現具體的緩存功能,如下所示:
?/***?Create?a?cache?of?at?most?{@code?maxSize}?bytes?in?{@code?directory}.*/public?Cache(File?directory,?long?maxSize)?{this(directory,?maxSize,?FileSystem.SYSTEM);}Cache(File?directory,?long?maxSize,?FileSystem?fileSystem)?{this.cache?=?DiskLruCache.create(fileSystem,?directory,?VERSION,?ENTRY_COUNT,?maxSize);}????????DiskLruCache 最終會將需要緩存的數據保存在本地。如果感覺 OkHttp 自帶的這套緩存策略太過復雜,我們可以設置使用 DiskLruCache 自己實現緩存機制。
LRU:是近期最少使用的算法(緩存淘汰算法),它的核心思想是當緩存滿時,會優先淘汰那些近期最少使用的緩存對象。采用LRU算法的緩存有兩種:LrhCache和DisLruCache,分別用于實現內存緩存和硬盤緩存,其核心思想都是LRU緩存算法。
CallServerInterceptor 詳解
????????CallServerInterceptor 是 OkHttp 中最后一個攔截器,也是 OkHttp 中最核心的網路請求部分。
CallServerInterceptor.intercept
??@Override?public?Response?intercept(Chain?chain)?throws?IOException?{//獲取RealInterceptorChainRealInterceptorChain?realChain?=?(RealInterceptorChain)?chain;//獲取ExchangeExchange?exchange?=?realChain.exchange();Request?request?=?realChain.request();long?sentRequestMillis?=?System.currentTimeMillis();exchange.writeRequestHeaders(request);boolean?responseHeadersStarted?=?false;Response.Builder?responseBuilder?=?null;if?(HttpMethod.permitsRequestBody(request.method())?&&?request.body()?!=?null)?{if?("100-continue".equalsIgnoreCase(request.header("Expect")))?{exchange.flushRequest();responseHeadersStarted?=?true;exchange.responseHeadersStart();responseBuilder?=?exchange.readResponseHeaders(true);}if?(responseBuilder?==?null)?{if?(request.body().isDuplex())?{exchange.flushRequest();BufferedSink?bufferedRequestBody?=?Okio.buffer(exchange.createRequestBody(request,?true));request.body().writeTo(bufferedRequestBody);}?else?{//?Write?the?request?body?if?the?"Expect:?100-continue"?expectation?was?met.BufferedSink?bufferedRequestBody?=?Okio.buffer(exchange.createRequestBody(request,?false));request.body().writeTo(bufferedRequestBody);bufferedRequestBody.close();}}?else?{exchange.noRequestBody();if?(!exchange.connection().isMultiplexed())?{//?If?the?"Expect:?100-continue"?expectation?wasn't?met,?prevent?the?HTTP/1?connection//?from?being?reused.?Otherwise?we're?still?obligated?to?transmit?the?request?body?to//?leave?the?connection?in?a?consistent?state.exchange.noNewExchangesOnConnection();}}}?else?{exchange.noRequestBody();}if?(request.body()?==?null?||?!request.body().isDuplex())?{exchange.finishRequest();}上面是向服務器端發送請求數據-----強大的分割線----------下面是從服務端獲取相應數據并構建?Response?對象if?(!responseHeadersStarted)?{exchange.responseHeadersStart();}if?(responseBuilder?==?null)?{responseBuilder?=?exchange.readResponseHeaders(false);}Response?response?=?responseBuilder.request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();int?code?=?response.code();if?(code?==?100)?{//?server?sent?a?100-continue?even?though?we?did?not?request?one.//?try?again?to?read?the?actual?responseresponse?=?exchange.readResponseHeaders(false).request(request).handshake(exchange.connection().handshake()).sentRequestAtMillis(sentRequestMillis).receivedResponseAtMillis(System.currentTimeMillis()).build();code?=?response.code();}exchange.responseHeadersEnd(response);if?(forWebSocket?&&?code?==?101)?{//?Connection?is?upgrading,?but?we?need?to?ensure?interceptors?see?a?non-null?response?body.response?=?response.newBuilder().body(Util.EMPTY_RESPONSE).build();}?else?{response?=?response.newBuilder().body(exchange.openResponseBody(response)).build();}。。。return?response;}小結
????????首先 OkHttp 內部是一個門戶模式,所有的下發工作都是通過一個門戶 Dispatcher 來進行分發。
????????然后在網絡請求階段通過責任鏈模式,鏈式的調用各個攔截器的 intercept 方法。重點介紹了 2 個比較重要的攔截器:CacheInterceptor 和 CallServerInterceptor。它們分別用來做請求緩存和執行網絡請求操作。
往期回顧?
RecyclerView 繪制流程及Recycler緩存
Glide 緩存機制及源碼(二)
Glide 的簡單使用(一)
總結
以上是生活随笔為你收集整理的Android OkHttp使用和源码详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么很多的企业留不住人才呢?
- 下一篇: CVPR论文解读 | 点云匹配的旋转不变