日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Okhttp-interceptor源码分析,快上车!

發(fā)布時(shí)間:2025/3/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Okhttp-interceptor源码分析,快上车! 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前言:被一位阿里面試官問(wèn)到過(guò)這個(gè)問(wèn)題,“讓我談一下okhhtp攔截器的調(diào)用順序及影響?!蔽夷X子里只有責(zé)任鏈的設(shè)計(jì)模式,但是幾種攔截器的順序我確實(shí)沒(méi)記憶過(guò),有點(diǎn)尷尬。所以呢,就來(lái)稍微研究一下,順便記憶一下,正所謂“面試造航母,工作擰螺絲。” 復(fù)制代碼

1)基本用法

此次我看的源碼是v3.9.0, 首先我們來(lái)看一下官網(wǎng)上基本用法(get請(qǐng)求為例),如下

OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build();Response response = client.newCall(request).execute();String jsonStr=response.body().string()復(fù)制代碼

可以看到主要分四步:1.構(gòu)建OkHttpClient對(duì)象;2.構(gòu)建Request對(duì)象;3.調(diào)用client.newCall(request)得到Call對(duì)象;4.Call對(duì)象執(zhí)行同步方法execute(),或者異步方法enqueue(Callback responseCallback),得到響應(yīng)的內(nèi)容.

2)請(qǐng)求過(guò)程源碼分析

2.1 OkhttpClient構(gòu)建

public OkHttpClient() {this(new Builder());}OkHttpClient(Builder builder) {this.dispatcher = builder.dispatcher;this.proxy = builder.proxy;this.protocols = builder.protocols;this.connectionSpecs = builder.connectionSpecs;this.interceptors = Util.immutableList(builder.interceptors);this.networkInterceptors = Util.immutableList(builder.networkInterceptors);this.eventListenerFactory = builder.eventListenerFactory;this.proxySelector = builder.proxySelector;this.cookieJar = builder.cookieJar;this.cache = builder.cache;this.internalCache = builder.internalCache;this.socketFactory = builder.socketFactory;...剔除部分源碼this.hostnameVerifier = builder.hostnameVerifier;this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner);this.proxyAuthenticator = builder.proxyAuthenticator;this.authenticator = builder.authenticator;this.connectionPool = builder.connectionPool;this.dns = builder.dns;this.followSslRedirects = builder.followSslRedirects;this.followRedirects = builder.followRedirects;this.retryOnConnectionFailure = builder.retryOnConnectionFailure;this.connectTimeout = builder.connectTimeout;this.readTimeout = builder.readTimeout;this.writeTimeout = builder.writeTimeout;this.pingInterval = builder.pingInterval; ....}public static final class Builder {Dispatcher dispatcher;@Nullable Proxy proxy;List<Protocol> protocols;List<ConnectionSpec> connectionSpecs;final List<Interceptor> interceptors = new ArrayList<>();final List<Interceptor> networkInterceptors = new ArrayList<>();EventListener.Factory eventListenerFactory;ProxySelector proxySelector;CookieJar cookieJar;@Nullable Cache cache;@Nullable InternalCache internalCache;SocketFactory socketFactory;@Nullable SSLSocketFactory sslSocketFactory;@Nullable CertificateChainCleaner certificateChainCleaner;HostnameVerifier hostnameVerifier;CertificatePinner certificatePinner;Authenticator proxyAuthenticator;Authenticator authenticator;ConnectionPool connectionPool;Dns dns;boolean followSslRedirects;boolean followRedirects;boolean retryOnConnectionFailure;int connectTimeout;int readTimeout;int writeTimeout;int pingInterval;public Builder() {dispatcher = new Dispatcher();protocols = DEFAULT_PROTOCOLS;connectionSpecs = DEFAULT_CONNECTION_SPECS;eventListenerFactory = EventListener.factory(EventListener.NONE);proxySelector = ProxySelector.getDefault();cookieJar = CookieJar.NO_COOKIES;socketFactory = SocketFactory.getDefault();hostnameVerifier = OkHostnameVerifier.INSTANCE;certificatePinner = CertificatePinner.DEFAULT;proxyAuthenticator = Authenticator.NONE;authenticator = Authenticator.NONE;connectionPool = new ConnectionPool();dns = Dns.SYSTEM;followSslRedirects = true;followRedirects = true;retryOnConnectionFailure = true;connectTimeout = 10_000;readTimeout = 10_000;writeTimeout = 10_000;pingInterval = 0;}Builder(OkHttpClient okHttpClient) {this.dispatcher = okHttpClient.dispatcher;this.proxy = okHttpClient.proxy;this.protocols = okHttpClient.protocols;this.connectionSpecs = okHttpClient.connectionSpecs;this.interceptors.addAll(okHttpClient.interceptors);this.networkInterceptors.addAll(okHttpClient.networkInterceptors);this.eventListenerFactory = okHttpClient.eventListenerFactory;this.proxySelector = okHttpClient.proxySelector;this.cookieJar = okHttpClient.cookieJar;this.internalCache = okHttpClient.internalCache;this.cache = okHttpClient.cache;this.socketFactory = okHttpClient.socketFactory;this.sslSocketFactory = okHttpClient.sslSocketFactory;this.certificateChainCleaner = okHttpClient.certificateChainCleaner;this.hostnameVerifier = okHttpClient.hostnameVerifier;this.certificatePinner = okHttpClient.certificatePinner;this.proxyAuthenticator = okHttpClient.proxyAuthenticator;this.authenticator = okHttpClient.authenticator;this.connectionPool = okHttpClient.connectionPool;this.dns = okHttpClient.dns;this.followSslRedirects = okHttpClient.followSslRedirects;this.followRedirects = okHttpClient.followRedirects;this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure;this.connectTimeout = okHttpClient.connectTimeout;this.readTimeout = okHttpClient.readTimeout;this.writeTimeout = okHttpClient.writeTimeout;this.pingInterval = okHttpClient.pingInterval;}public OkHttpClient build() {return new OkHttpClient(this);} } 復(fù)制代碼

可以看到Client對(duì)象是通過(guò)建造者模式設(shè)計(jì)的,直接new OkhttpClient()方式,傳入默認(rèn)的builder,或者通過(guò)配置builder,調(diào)用build()構(gòu)建;

2.2 client.newCall(Request request

Request對(duì)象也是通過(guò)建造者模式構(gòu)建的,根據(jù)不同的請(qǐng)求構(gòu)建不同的Request對(duì)象;

@Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);} /** 我們可以看到,client調(diào)用.newCall(request),實(shí)際調(diào)用的是RealCall.newRealCall(this, request, false),返回Call對(duì)象,說(shuō)明Realcall應(yīng)該是實(shí)現(xiàn)了Call接口; 在去看一下RealCall.newCall都干了些什么; */final class RealCall implements Call { ...static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {//可以看到RealCall實(shí)現(xiàn)了Call接口,newRealCall 內(nèi)部實(shí)際是將入?yún)魅霕?gòu)造,構(gòu)建了RealCall對(duì)象,創(chuàng)建一個(gè)call的事件監(jiān)聽(tīng);在看看RealCall構(gòu)造干了什么;RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call;}private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {//內(nèi)部將入?yún)lient,原始的請(qǐng)求賦值,同時(shí)構(gòu)建了RetryAndFollowUpInterceptor()攔截器;//該攔截器的作用就是發(fā)生錯(cuò)誤時(shí),進(jìn)行重定向;this.client = client;this.originalRequest = originalRequest;this.forWebSocket = forWebSocket;this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);}}復(fù)制代碼

2.3 call.excute() call.enqueue(callback)

我們繼續(xù)跟進(jìn)拿到call對(duì)象后,真正發(fā)起網(wǎng)絡(luò)請(qǐng)求的操作 繼續(xù)看call的實(shí)現(xiàn)類(lèi)RealCall

@Override public Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();//開(kāi)始監(jiān)聽(tīng)call請(qǐng)求eventListener.callStart(this);try {//通過(guò)client的dispatcher,分發(fā)請(qǐng)求;Dispather中構(gòu)建了一個(gè)線(xiàn)程池,所有的請(qǐng)求會(huì)加入到線(xiàn)程池中執(zhí)行; //這里client.dispatcher().executed(this);//這個(gè)方法很關(guān)鍵,也是Okhttp攔截器的責(zé)任鏈調(diào)用的開(kāi)始,是這個(gè)庫(kù)的精華所在,可以看到,在這里返回了服務(wù)器響應(yīng)的內(nèi)容//我已經(jīng)迫不及待一探究竟了,哈哈哈哈哈哈= =Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} catch (IOException e) {eventListener.callFailed(this, e);throw e;} finally {client.dispatcher().finished(this);}}@Override public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace(); //開(kāi)始監(jiān)聽(tīng)eventListener.callStart(this); //異步調(diào)用client.dispatcher().enqueue(new AsyncCall(responseCallback));/** *dispatcher對(duì)象的enqueue方法,可以看到,如果保存異步Call的集合小于最大請(qǐng)求數(shù),并且未達(dá)到同一個(gè)host最大的連接數(shù) *那么直接加入待執(zhí)行隊(duì)列,調(diào)用execute,執(zhí)行;否則,先加入預(yù)執(zhí)行隊(duì)列 */ synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}}}復(fù)制代碼

2.4 攔截器責(zé)任鏈的調(diào)用 getResponseWithInterceptorChain()

為了方便大家理解,請(qǐng)?jiān)试S我先上個(gè)圖:

Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors. //1.構(gòu)建一個(gè)空集合interceptorsList<Interceptor> interceptors = new ArrayList<>();//2.可以看到首先會(huì)將client的interceptors全部加入這個(gè)集合頭部,這個(gè)client的interceptors就是我們?cè)跇?gòu)建Client Builder時(shí),調(diào)用 // addInterceptor(Interceptor interceptor)加入的自定義攔截器,也就是所謂的應(yīng)用攔截器;interceptors.addAll(client.interceptors()); //3.尾部追加重定向攔截器 retryAndFollowUpInterceptor,還記得這個(gè)攔截器是什么時(shí)候創(chuàng)建的嗎? //沒(méi)錯(cuò),就是在new RealCall的時(shí)候構(gòu)建的.interceptors.add(retryAndFollowUpInterceptor); //4.尾部添加BridgeInterceptor,設(shè)置默認(rèn)的cookjar,這是一個(gè)請(qǐng)求和響應(yīng)的連接器interceptors.add(new BridgeInterceptor(client.cookieJar())); //5.添加緩存攔截器,處理一些網(wǎng)絡(luò)緩存邏輯,控制走網(wǎng)咯還是走緩存等interceptors.add(new CacheInterceptor(client.internalCache())); //6.添加網(wǎng)絡(luò)連接攔截器interceptors.add(new ConnectInterceptor(client)); //7.如果不是websocket,還添加了網(wǎng)絡(luò)攔截器,添加自定義連接攔截器(比如我們常用的facebook調(diào)試框架的開(kāi)源框架stecho)if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());} //8.添加了請(qǐng)求服務(wù)攔截器interceptors.add(new CallServerInterceptor(forWebSocket)); // 以上是存放在arryList中intercptor的順序, //9.可以看到接下來(lái)就是構(gòu)建責(zé)任鏈Chain, 其實(shí)現(xiàn)類(lèi)是RealInterceptorChain, // RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, // HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call, // EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) //10.其中,我們最主要來(lái)關(guān)注一下 index這個(gè)入?yún)?//可以看到,鏈的開(kāi)始index入?yún)?,我們繼續(xù)跟進(jìn)一下chain.proceed(originalRequest)干了什么;Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());return chain.proceed(originalRequest);} /*==========================*/public Builder addInterceptor(Interceptor interceptor) {if (interceptor == null) throw new IllegalArgumentException("interceptor == null");interceptors.add(interceptor);return this;}public List<Interceptor> interceptors() {return interceptors;}//進(jìn)入Chain的實(shí)現(xiàn)類(lèi)RealInterceptorChain,看一下proceed做了哪些事情 //我們看一些關(guān)鍵的地方.public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException { //11.首先判斷index是否超出攔截器索引,超出就跑異常,終止責(zé)任鏈if (index >= interceptors.size()) throw new AssertionError();calls++;// If we already have a stream, confirm that the incoming request will use it.if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)+ " must retain the same host and port");}// If we already have a stream, confirm that this is the only call to chain.proceed().if (this.httpCodec != null && calls > 1) {throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)+ " must call proceed() exactly once");}// 12.構(gòu)建下一個(gè)chain對(duì)象,可以看到此處index+1,RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);//13.從集合中取出下一個(gè)攔截器,調(diào)用 interceptor.intercept(chain)返回Response;我們可以隨便看一個(gè)interceptor的實(shí)現(xiàn)類(lèi), //研究一下interceptor.intercept(chain) 是如何返回Response的;Interceptor interceptor = interceptors.get(index);Response response = interceptor.intercept(next);// Confirm that the next interceptor made its required call to chain.proceed().if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {throw new IllegalStateException("network interceptor " + interceptor+ " must call proceed() exactly once");}// Confirm that the intercepted response isn't null.if (response == null) {throw new NullPointerException("interceptor " + interceptor + " returned null");}if (response.body() == null) {throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");}return response;}復(fù)制代碼

2.5 攔截器責(zé)任鏈的調(diào)用 interceptor.intercept(chain)

我們就以CacheInterceptor為例,看看它的intercept() fun都做了哪些事情.

@Override public Response intercept(Chain chain) throws IOException {...省略部分源碼,我們只看關(guān)鍵代碼//1.可以看到這里判斷了網(wǎng)絡(luò)請(qǐng)求是否存在,是否有響應(yīng)緩存,兩則都null,時(shí),構(gòu)建一個(gè)504錯(cuò)誤碼的Resonse并返回 // 該方法returnif (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();}// 2.判斷無(wú)請(qǐng)求網(wǎng)絡(luò),返回緩存內(nèi)容,方法returnif (networkRequest == null) {return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}//3.上述兩種情況都不滿(mǎn)足,走到這里,調(diào)用chain.proceed(request) //看到這個(gè)Api是否似曾相識(shí)呢~~哈哈哈哈哈,回到getResponseWithInterceptorChain()方法,其就是調(diào)用了 //chain.proceed(originalRequest)得到Response返回; //而,chain.proceed 中如上分析,會(huì)把index++,構(gòu)建下一chain,至此,責(zé)任鏈就完整形成了. //前一個(gè)chain.proceed 返回值依賴(lài)后一個(gè)chain.proceed返回,由此遞歸,直到index 超出拋出超異常.Response networkResponse = null;try {networkResponse = chain.proceed(networkRequest);} finally {// If we're crashing on I/O or otherwise, don't leak the cache body.if (networkResponse == null && cacheCandidate != null) {closeQuietly(cacheCandidate.body());}}....省略代碼return response;} 復(fù)制代碼

3)總結(jié)

我們可以看到Okhttp在構(gòu)建Client和Request對(duì)象時(shí)用到了Builder模式,這才我們開(kāi)發(fā)中也使用場(chǎng)景也很多,尤其是參數(shù)很多的情況下,通過(guò)構(gòu)造或者set方法初始化就不是特別友好;Interceptor的責(zé)任鏈模式設(shè)計(jì)模式也很巧妙,我們可以通過(guò)很輕松的添加或刪除攔截器實(shí)現(xiàn)許多業(yè)務(wù)場(chǎng)景,但是細(xì)想一下也不難發(fā)現(xiàn),責(zé)任鏈模式如果鏈太長(zhǎng)的情況下會(huì)發(fā)生什么?方法不斷壓棧,只有鏈調(diào)用結(jié)束或者異常時(shí),才會(huì)出棧,釋放??臻g. 有哪里寫(xiě)錯(cuò)或理解錯(cuò)誤的歡迎討論指出~~

總結(jié)

以上是生活随笔為你收集整理的Okhttp-interceptor源码分析,快上车!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。