Okhttp-interceptor源码分析,快上车!
1)基本用法
此次我看的源碼是v3.9.0, 首先我們來看一下官網上基本用法(get請求為例),如下
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build();Response response = client.newCall(request).execute();String jsonStr=response.body().string()復制代碼可以看到主要分四步:1.構建OkHttpClient對象;2.構建Request對象;3.調用client.newCall(request)得到Call對象;4.Call對象執行同步方法execute(),或者異步方法enqueue(Callback responseCallback),得到響應的內容.
2)請求過程源碼分析
2.1 OkhttpClient構建
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);} } 復制代碼可以看到Client對象是通過建造者模式設計的,直接new OkhttpClient()方式,傳入默認的builder,或者通過配置builder,調用build()構建;
2.2 client.newCall(Request request
Request對象也是通過建造者模式構建的,根據不同的請求構建不同的Request對象;
@Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);} /** 我們可以看到,client調用.newCall(request),實際調用的是RealCall.newRealCall(this, request, false),返回Call對象,說明Realcall應該是實現了Call接口; 在去看一下RealCall.newCall都干了些什么; */final class RealCall implements Call { ...static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {//可以看到RealCall實現了Call接口,newRealCall 內部實際是將入參傳入構造,構建了RealCall對象,創建一個call的事件監聽;在看看RealCall構造干了什么;RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call;}private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {//內部將入參client,原始的請求賦值,同時構建了RetryAndFollowUpInterceptor()攔截器;//該攔截器的作用就是發生錯誤時,進行重定向;this.client = client;this.originalRequest = originalRequest;this.forWebSocket = forWebSocket;this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);}}復制代碼2.3 call.excute() call.enqueue(callback)
我們繼續跟進拿到call對象后,真正發起網絡請求的操作 繼續看call的實現類RealCall
@Override public Response execute() throws IOException {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();//開始監聽call請求eventListener.callStart(this);try {//通過client的dispatcher,分發請求;Dispather中構建了一個線程池,所有的請求會加入到線程池中執行; //這里client.dispatcher().executed(this);//這個方法很關鍵,也是Okhttp攔截器的責任鏈調用的開始,是這個庫的精華所在,可以看到,在這里返回了服務器響應的內容//我已經迫不及待一探究竟了,哈哈哈哈哈哈= =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(); //開始監聽eventListener.callStart(this); //異步調用client.dispatcher().enqueue(new AsyncCall(responseCallback));/** *dispatcher對象的enqueue方法,可以看到,如果保存異步Call的集合小于最大請求數,并且未達到同一個host最大的連接數 *那么直接加入待執行隊列,調用execute,執行;否則,先加入預執行隊列 */ synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}}}復制代碼2.4 攔截器責任鏈的調用 getResponseWithInterceptorChain()
為了方便大家理解,請允許我先上個圖:
Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors. //1.構建一個空集合interceptorsList<Interceptor> interceptors = new ArrayList<>();//2.可以看到首先會將client的interceptors全部加入這個集合頭部,這個client的interceptors就是我們在構建Client Builder時,調用 // addInterceptor(Interceptor interceptor)加入的自定義攔截器,也就是所謂的應用攔截器;interceptors.addAll(client.interceptors()); //3.尾部追加重定向攔截器 retryAndFollowUpInterceptor,還記得這個攔截器是什么時候創建的嗎? //沒錯,就是在new RealCall的時候構建的.interceptors.add(retryAndFollowUpInterceptor); //4.尾部添加BridgeInterceptor,設置默認的cookjar,這是一個請求和響應的連接器interceptors.add(new BridgeInterceptor(client.cookieJar())); //5.添加緩存攔截器,處理一些網絡緩存邏輯,控制走網咯還是走緩存等interceptors.add(new CacheInterceptor(client.internalCache())); //6.添加網絡連接攔截器interceptors.add(new ConnectInterceptor(client)); //7.如果不是websocket,還添加了網絡攔截器,添加自定義連接攔截器(比如我們常用的facebook調試框架的開源框架stecho)if (!forWebSocket) {interceptors.addAll(client.networkInterceptors());} //8.添加了請求服務攔截器interceptors.add(new CallServerInterceptor(forWebSocket)); // 以上是存放在arryList中intercptor的順序, //9.可以看到接下來就是構建責任鏈Chain, 其實現類是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.其中,我們最主要來關注一下 index這個入參 //可以看到,鏈的開始index入參為0,我們繼續跟進一下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;}//進入Chain的實現類RealInterceptorChain,看一下proceed做了哪些事情 //我們看一些關鍵的地方.public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException { //11.首先判斷index是否超出攔截器索引,超出就跑異常,終止責任鏈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.構建下一個chain對象,可以看到此處index+1,RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);//13.從集合中取出下一個攔截器,調用 interceptor.intercept(chain)返回Response;我們可以隨便看一個interceptor的實現類, //研究一下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;}復制代碼2.5 攔截器責任鏈的調用 interceptor.intercept(chain)
我們就以CacheInterceptor為例,看看它的intercept() fun都做了哪些事情.
@Override public Response intercept(Chain chain) throws IOException {...省略部分源碼,我們只看關鍵代碼//1.可以看到這里判斷了網絡請求是否存在,是否有響應緩存,兩則都null,時,構建一個504錯誤碼的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.判斷無請求網絡,返回緩存內容,方法returnif (networkRequest == null) {return cacheResponse.newBuilder().cacheResponse(stripBody(cacheResponse)).build();}//3.上述兩種情況都不滿足,走到這里,調用chain.proceed(request) //看到這個Api是否似曾相識呢~~哈哈哈哈哈,回到getResponseWithInterceptorChain()方法,其就是調用了 //chain.proceed(originalRequest)得到Response返回; //而,chain.proceed 中如上分析,會把index++,構建下一chain,至此,責任鏈就完整形成了. //前一個chain.proceed 返回值依賴后一個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;} 復制代碼3)總結
我們可以看到Okhttp在構建Client和Request對象時用到了Builder模式,這才我們開發中也使用場景也很多,尤其是參數很多的情況下,通過構造或者set方法初始化就不是特別友好;Interceptor的責任鏈模式設計模式也很巧妙,我們可以通過很輕松的添加或刪除攔截器實現許多業務場景,但是細想一下也不難發現,責任鏈模式如果鏈太長的情況下會發生什么?方法不斷壓棧,只有鏈調用結束或者異常時,才會出棧,釋放棧空間. 有哪里寫錯或理解錯誤的歡迎討論指出~~
總結
以上是生活随笔為你收集整理的Okhttp-interceptor源码分析,快上车!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端常用效果(2)带效果的tab切换
- 下一篇: ES6学习02