OkHttp built-in interceptor
In this blog OKHTTP3 Interceptor (Interceptor), we have introduced the role of interceptors, interceptors are OkHttp provides a powerful mechanism for the unified processing of HTTP requests and responses, it can achieve network monitoring, request and response rewriting, request failure enrichment and other functions.
We also know that interceptors can be linked together, and we can register custom interceptors (application interceptors and network interceptors) to the interceptor chain, such as:
In fact, in addition to our custom interceptors, several other interceptors are available inside the OkHttp system, which is part of the OkHttp core. OkHttp internal interceptors are responsible for different functions, each of which is a interceptor that connects to form an interceptor chain and, ultimately, completes a network request.
Specific example:
In the previous blog OKHTTP3 source analysis, we analyzed the OkHttp synchronous and asynchronous request flow source, found whether the synchronous request or asynchronous request is through the call Realcall Getresponsewithinterceptorchain () method to get the response response.
Realcall. Getresponsewithinterceptorchain () Source code:
Response Getresponsewithinterceptorchain () throws IOException {list<interceptor> interceptors = new ArrayList (); Add a custom app blocker interceptors.addall (This.client.interceptors ()); Interceptor Interceptors.add (This.retryandfollowupinterceptor) responsible for redirection and failed retries; Bridging the network layer and the application layer is the addition of the HTTP requests that are required by the user to add some service-side interceptors.add (New Bridgeinterceptor (This.client.cookieJar ())); Responsible for reading the cache, updating the cache Interceptors.add (new Cacheinterceptor (This.client.internalCache ())); Responsible for establishing connection with the server Interceptors.add (new Connectinterceptor (this.client)); Configure a custom network interceptor if (!this.forwebsocket) {Interceptors.addall (this.client.networkInterceptors ()); }//Send a request to the server to read the response data Interceptors.add (new Callserverinterceptor (This.forwebsocket)) from the service side; Create the Interceptor Chain Chain object, where the List collection of various interceptors is passed in Chain Chain = new Realinterceptorchain (interceptors, (streamallocation) null, (H TTPCODEC) NULL, (realconnection) null, 0, this.originalrequest, this, This.eventlistener, This.client.connectTimeoutMillis(), This.client.readTimeoutMillis (), This.client.writeTimeoutMillis ()); Get response return Chain.proceed (this.originalrequest) via chained request;}
In this method, we found the OkHttp built-in interceptors, the specific role of these interceptors later, the first to macro analysis of what Getresponsewithinterceptorchain () do what work:
- Creates a series of interceptors and puts them into an interceptor list collection.
- An interceptor chain Realinterceptorchain is created by passing the list of interceptors into the Realinterceptorchain construction method.
- Execute the proceed () method of the Interceptor chain chain to invoke the interceptor of each different function in turn, and finally get the response.
So how does this Chain object handle the Interceptor collection, and why is it possible to get the response after the interceptor chain is processed by invoking Chain.proceed?
In fact, the answer to this question is the responsibility chain design mode, it is recommended to first understand about the responsibility chain model introduction, and then look back down.
After understanding the chain of responsibility model, we can easily understand how interceptors work.
First look at the Interceptor interface, it is obvious that it is the role of abstract processor in the chain of responsibility, all kinds of interceptors need to implement its intercept method
/** * Observes, modifies, and potentially short-circuits requests going out and the corresponding * responses coming back in. Typically interceptors add, remove, or transform headers on the request * or response. */public interface Interceptor { Response intercept(Chain chain) throws IOException; interface Chain { Request request(); Response proceed(Request request) throws IOException; ... }}
Here we notice that the interceptor also contains an internal interface Chain, by looking at the Chain interface, or about its capabilities:
- Obtaining request requests through the request () method
- Requests are processed by the proceed (request) method, and the response response is returned
Just introduced in the Getresponsewithinterceptorchain () method, it is up to Chain to call the interceptor in turn to get the response:
//创建 拦截器链chain 对象,这里将各种拦截器的 List 集合传了进去 Chain chain = new RealInterceptorChain(interceptors, (StreamAllocation)null, (HttpCodec)null, (RealConnection)null, 0, this.originalRequest, this, this.eventListener, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis()); //通过链式请求得到 response return chain.proceed(this.originalRequest);
So how does it work in particular? Let's take a look at how the Realinterceptorchain is constructed.
Public final class Realinterceptorchain implements Interceptor.chain {private final list<interceptor> Interceptor S Private final streamallocation streamallocation; Private final Httpcodec Httpcodec; Private final realconnection connection; private final int index; private final request request; Private final call call; Private final EventListener EventListener; private final int connecttimeout; private final int readtimeout; private final int writetimeout; private int calls; Public Realinterceptorchain (list<interceptor> interceptors, streamallocation streamallocation, HttpCodec HttpC Odec, realconnection connection, int index, request request, call call, EventListener eventlistener, int Connecttimeo UT, int readtimeout, int writetimeout) {this.interceptors = interceptors; This.connection = connection; This.streamallocation = streamallocation; This.httpcodec = Httpcodec; This.index = index; This.request = Request; This.call = Call; This.eventlistener = EventListener; This.connecttimeout = ConnectTimeout; This.readtimeout = ReadTimeout; This.writetimeout = WriteTimeout; }
It is important to note that the index parameter in This construction method, the index passed to the construction method, is finally assigned to a global variable index (this variable is significant and will be used later). After the Realinterceptorchain object has been constructed, it is then called by the proceed method to execute the interceptor.
Take a look at the specific implementation of the Chain.proceed (request) Method:
Realinterceptorchain#proceed:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); calls++; ... // Call the next interceptor in the chain. RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout); //从拦截器集合中获取当前拦截器 Interceptor interceptor = interceptors.get(index); //调用当前的拦截器的 intercept 方法获取 response Response response = interceptor.intercept(next); ... return response; }
The key logic of this method in this with these three lines of code
One, in the Chain.proceed method, and new a realinterceptorchain, but here the parameter is index + 1, that is, each call proceed method, will produce an index member to change The Realinterceptorchain object of volume +1. and the name of the chain object is next, so we can guess roughly that it represents the next chain object.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout);
Second, obtain the current interceptor based on the index value, which is the index value of the previous creation of the chain constructor.
You should remember that when Getresponsewithinterceptorchain first created the Chain object, index was initialized to 0.
Interceptor interceptor = interceptors.get(index);
Third, call the Intercept (Chain Chain) method of the current interceptor
Response response = interceptor.intercept(next);
Here we take index 0 as an example, get the first interceptor Retryandfollowupinterceptor in the Interceptors collection (assuming no user-defined app interceptors are added) and look at its intercept method:
@Override public Response Intercept (Chain Chain) throws IOException {Request request = Chain.request (); Realinterceptorchain Realchain = (realinterceptorchain) chain; Call call = Realchain.call (); EventListener EventListener = Realchain.eventlistener (); Streamallocation streamallocation = new Streamallocation (Client.connectionpool (), Createaddress (Request.url ()), Cal L, EventListener, callstacktrace); This.streamallocation = streamallocation; int followupcount = 0; Response priorresponse = null; while (true) {if (canceled) {streamallocation.release (); throw new IOException ("Canceled"); } Response Response; Boolean releaseconnection = true; try {response = Realchain.proceed (request, streamallocation, NULL, NULL); Releaseconnection = false; } catch (Routeexception e) {//The attempt to connect via a route failed. The request won't have been sent. if (!recover (E.getlastconnecTexception (), streamallocation, False, request) {throw e.getfirstconnectexception (); } releaseconnection = false; Continue } catch (IOException e) {//an attempt to communicate with a server failed. The request may be been sent. Boolean requestsendstarted =! (e instanceof connectionshutdownexception); if (!recover (E, streamallocation, requestsendstarted, request)) throw E; Releaseconnection = false; Continue } finally {//We ' re throwing an unchecked exception. Release any resources. if (releaseconnection) {streamallocation.streamfailed (null); Streamallocation.release (); } } ... } }
The key points in this code are:
response = realChain.proceed(request, streamAllocation, null, null);
We found that the Chain.proceed () method was called in intercept (), and each time the proceed method was called, the next interceptor indexed as Index + 1 was fetched, and the Intercept () method of executing the interceptor was used to Recursive invocation of the Interceptor, which implements a progressive call to interceptors.
The process flow chart is as follows:
Here we may have a question, that is why every time we need to create a new Realinterceptorchain object, only need to modify the value of the index variable is not also achieve the same effect? The reason for this is that the Realinterceptorchain object also contains additional information such as request requests, and each time the Intercept method of the interceptor is executed, the intercept of this layer is not executed because of the recursive call, if reuse Realinterceptorchain objects, other hierarchies affect Realinterceptorchain objects at this level.
Reference
79643123
79008433
OKHTTP3 Interceptor Source Code Analysis