Interceptor chain should be one of the core of okhttp framework.
Responsibility chain model
The implementation of okhttp request Interceptor is a typical responsibility Chain mode, which generally requires the Interceptor class to implement the same interface. There is an Interceptor interface in okhttp. Both the default Interceptor and our custom Interceptor should implement this interface. The Interceptor interface also has an internal interface Chain. In okhttp, its implementation class RealInterceptorChain is generally used
The following is a partial code of the Interceptor interface:
public interface Interceptor { //The chain contains objects such as request. Requests are processed and passed through the chain Response intercept(Chain chain) throws IOException; interface Chain { //Returns the request held by the chain Request request(); //Pass the request to the next interceptor Response proceed(Request request) throws IOException; ........................ } }
Source code analysis
In the last paragraph of the important method getResponseWithInterceptorChain() mentioned in the previous section, we build a RealInterceptorChain object and call its processed() method to obtain the Response.
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); /* The following is the constructor of RealInterceptorChain. The parameters in it need to be passed in the interceptor chain */ public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call, EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) { this.interceptors = interceptors; this.connection = connection; this.streamAllocation = streamAllocation; this.httpCodec = httpCodec; this.index = index;//Index, and then select the corresponding interceptor according to the index this.request = request; this.call = call; this.eventListener = eventListener; this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; this.writeTimeout = writeTimeout; }
This is the first time to call the proceed method, pass in the original request, and notify the next interceptor to process it. If we do not add a custom interceptor, generally speaking, the first interceptor to process the request will be the RetryAndFollowUpInterceptor, which is mainly used to build the streamalallocation and complete the request retry and redirection after receiving the response.
Let's take a direct look at the processed() method of RealInterceptorChain:
//Methods in RealInterceptorChain public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError(); //If the index number is greater than the length of the interception chain, an exception is thrown calls++; // If we already have a stream, confirm that the incoming request will use it. //Ensure that new incoming requests will use the existing stream 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(). //Only one request can call the interception chain for execution if (this.httpCodec != null && calls > 1) { throw new IllegalStateException("network interceptor " + interceptors.get(index - 1) + " must call proceed() exactly once"); } // Call the next interceptor in the chain. //When the next interceptor in the interception chain is called, the corresponding interceptor will be obtained according to the index parameter of the next object below RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request, call, eventListener, connectTimeout, readTimeout, writeTimeout);//Note this index + 1 Interceptor interceptor = interceptors.get(index);//Take interceptor according to index //If there is no custom interceptor, the first thing to get is the redirect interceptor, and then the intercept method of each interceptor will be called //Call the intercept method of the interceptor and get the returned response Response response = interceptor.intercept(next); //The following is the returned response // Confirm that the next interceptor made its required call to chain.proceed(). //Make sure that the next interceptor does call 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. //Ensure that the returned response is not null if (response == null) { throw new NullPointerException("interceptor " + interceptor + " returned null"); } //The body of the response cannot be null if (response.body() == null) { throw new IllegalStateException( "interceptor " + interceptor + " returned a response with no body"); } return response; } //The following is part of the code in the intercept method of RetryAndFollowUpInterceptor //It can be seen that generally, the intercept method will recursively call the processed method and pass the request to the next interceptor ............ Response response;//Returned response boolean releaseConnection = true; try {//Pass in request and give it to response = realChain.proceed(request, streamAllocation, null, null); releaseConnection = false; } ................
summary
After initializing a RealInterceptorChain object at okhttp, you can pass in the RealInterceptorChain object by calling the proceed() method. In the proceed() method, you will call the Intercept method of the interceptor to notify the next interceptor to set the properties of the RealInterceptorChain object (mainly processing request and response).
(Note: the RealInterceptorChain object has an int variable named index, and the interceptor can be selected correctly through the attribute index. For example, if index = 0, it means that the first interceptor is selected.)
Intercept in the interceptor will recursively call the processed () method to pass the RealInterceptorChain object to the next object for processing.
The work of Intercept method can be divided into two parts. The first part is before calling the proceed() method. The task of this part is mainly to prepare before sending a request to the server. The second part is to process the response returned by the proceed() method (that is, the response returned by the server passed from the interceptor below) after calling the proceed() method.
Brief process: