In the previous article in this series, we learned the basics of OkHttp, experienced the power of this network loading framework, and its very simple API. For those of you who haven’t read the previous article, I recommend reading the OkHttp framework full solution series – (1) Basic use of OkHttp.
If we wanted to make a GET request, we could do so with a little code, as follows:
OkHttpClient httpClient = new OkHttpClient();
String url = "https://www.baidu.com/";
Request getRequest = new Request.Builder()
.url(url)
.get()
.build();
Call call = httpClient.newCall(getRequest);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {}});Copy the code
What looks like neat code is actually OkHttp doing tons of work behind the scenes.
Know why and know why. In this article, we’ll take a look at the source code for OkHttp and see how complex it is behind these simple uses.
Request creation
With the code steps for the GET request above, let’s first examine the creation of an OkHttpClient instance. In the previous article, we mentioned that OkHttpClient instantiations can be created directly or configured using Builder Builder mode and then built (). Direct creation is simply using the default configuration. Its construction method is as follows:
public OkHttpClient(a) {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;// Scheduler, used to control concurrent requests. Calls for both synchronous and asynchronous requests are stored internally, and asynchronous requests are handled using a thread pool.
this.proxy = builder.proxy;// Proxy Settings
this.protocols = builder.protocols;/ / HTTP protocol
this.connectionSpecs = builder.connectionSpecs;// Connection configuration
this.interceptors = Util.immutableList(builder.interceptors);// Global interceptor
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);// Network interceptor
this.eventListenerFactory = builder.eventListenerFactory;// Create factory for requested listeners
this.proxySelector = builder.proxySelector;// Proxy selector
this.cookieJar = builder.cookieJar;//cookie, no cookie by default: cookiejar.no_cookies
this.cache = builder.cache;// Network cache Settings
this.internalCache = builder.internalCache;// Cache interface for internal use
this.socketFactory = builder.socketFactory;/ / socket factory
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if(builder.sslSocketFactory ! =null| |! isTLS) {this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if(sslSocketFactory ! =null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;// These are security related Settings
this.connectionPool = builder.connectionPool;/ / the connection pool
this.dns = builder.dns;// domain name resolution system
this.followSslRedirects = builder.followSslRedirects;/ / SSL redirection
this.followRedirects = builder.followRedirects;/ / redirection
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;// Whether to retry if the connection fails
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;// Several timeout Settings
this.pingInterval = builder.pingInterval;// Ping interval
// Interceptors cannot be null
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: "+ networkInterceptors); }}public static final class Builder {...public Builder(a) {
// Default configuration
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
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;
callTimeout = 0;
connectTimeout = 10 _000;
readTimeout = 10 _000;
writeTimeout = 10 _000;
pingInterval = 0; }... }Copy the code
Create the OkHttpClient instance directly with the configuration item as the default in the Builder constructor. As you can see, the configuration items are numerous, including timeout Settings and interceptors that were used in the previous article.
Let’s look at the Request creation, again using the builder pattern:
public Builder(a) {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder get(a) {
return method("GET".null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder method(String method, @Nullable RequestBody body) {
if (method == null) throw new NullPointerException("method == null");
if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
if(body ! =null && !HttpMethod.permitsRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must not have a request body.");
}
if (body == null && HttpMethod.requiresRequestBody(method)) {
throw new IllegalArgumentException("method " + method + " must have a request body.");
}
this.method = method;
this.body = body;
return this;
}
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder url(String url) {
if (url == null) throw new NullPointerException("url == null");
// Silently replace web socket URLs with HTTP URLs.
if (url.regionMatches(true.0."ws:".0.3)) {
url = "http:" + url.substring(3);
} else if (url.regionMatches(true.0."wss:".0.4)) {
url = "https:" + url.substring(4);
}
return url(HttpUrl.get(url));
}
public Request build(a) {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
Copy the code
Get () and POST (RequestBody body) are wrapped around method methods. The method method validates the request type and body. For example, get requests cannot have a body, and POST requests must have a body. Others are easier to understand and will not be repeated.
HttpClient’s newCall method:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
Copy the code
Follow RealCall’s newRealCall method:
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;
}
Copy the code
See that HttpClient’s newCall method gets a Call that is actually a RealCall. RealCall is a request ready to be executed and is an implementation of the Call interface. It holds the OkHttpClient instance and Request instance internally. And there is also a Transmitter created to assign a value to the Transmitter of RealCall.
As a bridge between the application layer and the network layer, it plays a very important role in connecting, really sending requests and reading responses. Let’s look at the construction method:
public Transmitter(OkHttpClient client, Call call) {
this.client = client;
this.connectionPool = Internal.instance.realConnectionPool(client.connectionPool());
this.call = call;
this.eventListener = client.eventListenerFactory().create(call);
this.timeout.timeout(client.callTimeoutMillis(), MILLISECONDS);
}
Copy the code
Internal Transmitter holds OkHttpClient, connection pool, call, and event listener.
Back to the RealCall interface, Call:
// The request is ready to be executed. Because it represents a single request/response pair (stream), it cannot be executed twice
public interface Call extends Cloneable {...// The synchronization request will block
Response execute(a) throws IOException;
// Asynchronous request
void enqueue(Callback responseCallback);
// Cancel the request.
void cancel(a);
boolean isExecuted(a);
boolean isCanceled(a);
Timeout timeout(a); .interface Factory {
Call newCall(Request request); }}Copy the code
Basically, it defines the execution action and state of the request. RealCall’s implementation of Call is explained later in the execution process.
Well, that’s it for the creation of the request.
Scheduling of requests
The execution can be synchronous or asynchronous. We start with the synchronous request, that is, the execute method of RealCall:
@Override public Response execute(a) throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.timeoutEnter();// The timeout starts
transmitter.callStart();// The callback request listener starts
try {
client.dispatcher().executed(this);// Queue
return getResponseWithInterceptorChain();// Execute the request to get the result
} finally {
client.dispatcher().finished(this);// End of request}}Copy the code
First determine that if it has been executed, an exception will be thrown. This is why a request can only be executed once. Then the callback request listener begins. Then call the executed method of the client Dispatcher:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
Copy the code
Quite simply, the request is put into a two-ended queue, runningSyncCalls, which represents the synchronous request being executed.
And then returned to the getResponseWithInterceptorChain () the result of the Response, you can guess, a synchronous request the real request process is in getResponseWithInterceptorChain approach. Finished (Deque calls, T call)
// End the asynchronous request
void finished(AsyncCall call) {
//callsPerHost-1
call.callsPerHost().decrementAndGet();
finished(runningAsyncCalls, call);
}
// End the synchronization request
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
// The end of asynchro, synchronous, will end here: remove from running and call promoteAndExecute
private <T> void finished(Deque<T> calls, T call) {...synchronized (this) {
// Remove from queue
if(! calls.remove(call))throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
booleanisRunning = promoteAndExecute(); . }Copy the code
The call is removed from the queue, and promoteAndExecute() is executed, but I won’t follow you through.
Here, we know, a synchronous request of getResponseWithInterceptorChain () method;
Let’s look at asynchronous requests, the enqueue method of RealCall:
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
transmitter.callStart();// The callback request listener starts
client.dispatcher().enqueue(new AsyncCall(responseCallback));// Request scheduling
}
Copy the code
Again, the callback request starts by checking whether the request has already been made. AsyncCall inherits NamedRunnable. NamedRunnable is implemented from Runnable. AsyncCall is a Runnable. You can imagine that it will execute the run method on a thread or thread pool. The run method is not seen in AsyncCall, but in NamedRunnable:
// Know the Runnable of the current thread name
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
public final void run(a) {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally{ Thread.currentThread().setName(oldName); }}protected abstract void execute(a);
}
Copy the code
Run calls the abstract method execute(). Execute () is implemented in AsyncCall.
Let’s move on to the Dispatcher enqueue method:
void enqueue(AsyncCall call) {
synchronized (this) {
// Put it into a queue waiting for execution
readyAsyncCalls.add(call);
// Requests to the same host share the same call count
if(! call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host());if(existingCall ! =null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
// Find a request with the same host from runningAsyncCalls or readyAsyncCalls
private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
Copy the code
The request is first placed in a two-ended queue called readyAsyncCalls, which represents asynchronous requests waiting to be executed. Why wait for execution? Leave a question. It then finds a request with the same host from either the running request runningAsyncCalls or the pending request readyAsyncCalls and reuses the callsPerHost for the current request. CallsPerHost looks like the number of requests that have the same host, and notices that the type is AtomicInteger, declared as follows:
private volatile AtomicInteger callsPerHost = new AtomicInteger(0);
Copy the code
Therefore, requests to the same host share the callsPerHost, in preparation for determining host concurrency later.
The promoteAndExecute() method is called as well as the finish method.
// The core method of scheduling: using thread pools to execute asynchronous requests based on the policy of controlling asynchronous concurrency
private boolean promoteAndExecute(a) {
assert(! Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Maximum number of concurrent requests 64
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; //Host Maximum number of concurrent requests 5
i.remove();// Remove from the wait queue
asyncCall.callsPerHost().incrementAndGet();//Host Number of concurrent requests +1
executableCalls.add(asyncCall);// Joins a collection of executable requests
runningAsyncCalls.add(asyncCall);// Join an asynchronous request queue in progress
}
isRunning = runningCallsCount() > 0;// Number of asynchronous/synchronous requests in progress >0
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());// Executable request
}
return isRunning;
}
public synchronized int runningCallsCount(a) {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
Copy the code
Two checks are made to iterate over readyAsyncCalls: break if the number of asynccalls in progress is greater than the maximum number of concurrent requests 64, or continue if the number of requests to the same host is greater than 5. If all checks pass, it is removed from the wait queue, the callsPerHost is incremented by 1, placed into the executable collection executableCalls, and added to the queue runningAsyncCalls, which represents asynchronous requests being executed. In order to control the maximum number of concurrent requests, 64 asynchronous requests and 5 asynchronous requests to the same host are placed in the queue.
Execute all executableCalls requests to the executeOn method:
void executeOn(ExecutorService executorService) {
assert(! Thread.holdsLock(client.dispatcher()));boolean success = false;
try {
executorService.execute(this);// Perform asyncCall in the thread pool
success = true;
} catch (RejectedExecutionException e) {
...
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);// The callback failed
} finally {
if(! success) { client.dispatcher().finished(this); // The execution is abnormal}}}// Thread pool definition
public synchronized ExecutorService executorService(a) {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher".false));
}
return executorService;
}
Copy the code
The executeOn method is simple: a request RealCall is performed using a thread pool similar to CachedThreadPool. If execution fails, the Dispatcher’s Finished (Deque calls, T Call) method is also called.
AsyncCall: execute(); AsyncCall: execute();
protected void execute(a) {
boolean signalledCallback = false;
transmitter.timeoutEnter();// The timeout starts
try {
Response response = getResponseWithInterceptorChain();//// Obtain the request result
responseCallback.onResponse(RealCall.this, response);// Callback result
} catch (IOException e) {
...
responseCallback.onFailure(RealCall.this, canceledException);// The callback failed. }finally {
client.dispatcher().finished(this);// End of request}}Copy the code
We found that here and a synchronous request is very like, is also called getResponseWithInterceptorChain () method to get Response as a result, the difference is the use of responseCallback results to go back to bring up, The dispatcher’s Finish method is also invoked to end the request.
In addition, as mentioned earlier, the promoteAndExecute() method is also called in the Finish method, indicating that the current asynchronous request is rescheduled when the synchronous/asynchronous request ends.
Ok, here we sort out the scheduling process:
- A synchronous request, use the Dispatcher in the runningSyncCalls RealCall, then use the getResponseWithInterceptorChain () to obtain the result, the final end request call the Dispatcher’s finish method.
- RealCall uses the Dispatcher to store readyAsyncCalls to host concurrency, and promoteAndExecute() is used to control asynchronous concurrency. Use a thread pool to execute asynchronous requests (concurrency control includes a maximum of 64 concurrent requests and a maximum of 5 concurrent requests to host). The implementation of the asynchronous request Also use the getResponseWithInterceptorChain (), after get results back to bring up. Finally call the Dispatcher finish method to end the request.
- PromoteAndExecute () is a Dispatcher that implements concurrent control of asynchronous requests and puts asynchronous requests into a thread pool. PromoteAndExecute () is called in two places: when an asynchronous request is added and when a synchronous/asynchronous request ends.
Execution of request
Here we go!
Through the above analysis, both synchronous and asynchronous request, the final execution are in RealCall getResponseWithInterceptorChain () method, only the asynchronous request Need to through the Dispatcher to manipulate the concurrency control and the thread pool. Then look at getResponseWithInterceptorChain () :
Response getResponseWithInterceptorChain(a) throws IOException {
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors()); // The user-configured application interceptor intercepts first
interceptors.add(new RetryAndFollowUpInterceptor(client));// Retry follow up interceptor
interceptors.add(new BridgeInterceptor(client.cookieJar()));// Bridge interceptor
interceptors.add(new CacheInterceptor(client.internalCache()));// Cache interceptor
interceptors.add(new ConnectInterceptor(client)); // Connect interceptor
if(! forWebSocket) { interceptors.addAll(client.networkInterceptors());// User-configured network interceptor
}
interceptors.add(new CallServerInterceptor(forWebSocket)); // Request service interceptor
// Interceptor chain
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);// The chain starts executing
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); }}}Copy the code
The first is to put the
- Apply the interceptor (external configuration) client.interceptors(),
- Try again to follow up blocker RetryAndFollowUpInterceptor,
- BridgeInterceptor,
- CacheInterceptor,
- ConnectInterceptor,
- Network interceptor (external configuration) client.net workInterceptors (),
- Request the service interceptor CallServerInterceptor,
Add it in turn to the collection Interceptors. Then use interceptors, transmitters, originalRequest, etc., to create a RealInterceptorChain instance, and use the proceed method to obtain the request result Response.
Interceptor was mentioned in the previous usage article, where the configured Interceptor is actually the application Interceptor: client.interceptors(), which was first added to interceptors. So what exactly is an interceptor? How does Chain.proceed get the result? Let’s take a look at the Interceptor class:
/ / the interceptor
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
// Interceptor chain
interface Chain {
Request request(a);
//Chain core method
Response proceed(Request request) throws IOException;
// Return the connection that requested execution. Only network interceptors are available; Applying interceptors is NULL.
@Nullable Connection connection(a);
Call call(a);
int connectTimeoutMillis(a);
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis(a);
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis(a);
Chain withWriteTimeout(int timeout, TimeUnit unit); }}Copy the code
Interceptor is an interface class that has only one Intercept method and takes a Chain object as an argument. Note that the internal interface Chain, the interceptor Chain, has a proceed method that takes a Request object and returns a Response. The implementation of this method is the processing of the Request. The only implementation class for Chain is RealInterceptorChain, which concatenates all interceptors. The PROCEED method is the concatenation operation.
The interceptors listed above are the implementation classes of Interceptor. Here are the interceptors implemented in the previous article:
new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
Response response = chain.proceed(request);
ResponseBody body = response.body();
Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
returnresponse; }}Copy the code
In the Intercept method we call chain.proceed to get the result and print some logs before and after. Where does the chain instance come from? When was the Intercept method called? — we’ll look back getResponseWithInterceptorChain method, all interceptors are introduced into RealInterceptorChain, speculation, to It must be the RealInterceptorChain’s proceed method that calls the intercept method internally. Check it out:
@Override public Response proceed(Request request) throws IOException {
return proceed(request, transmitter, exchange);
}
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange ! =null&&!this.exchange.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.exchange ! =null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if(exchange ! =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;
}
Copy the code
When instantiating the RealInterceptorChain, index is assigned 0 and Exchange is null, so the first three ifs are not entered. We then get the first interceptor, the application interceptor we configured, call its interceptor method, and return and verify the results. This confirms what we thought. Also note that the interceptor method that applies the interceptor takes the same parameters as the current interceptor instance: next, which simply adds index + 1. That is, next’s proceed method is called in our application interceptor.
Further, the proceed next method can also obtain the interceptors index = 1 interceptors, namely RetryAndFollowUpInterceptor instance, and then call the interceptor method, The index+1 parameter is the chain with index=2. Following up the code found RetryAndFollowUpInterceptor interceptor method inside is have proceed method invocation chain. This is passed in turn until the last interceptor, CallServerInterceptor.
In fact, all interceptor methods except the last one, CallServerInterceptor, call the proceed method passed into the chain. Each interceptor handles its own work before and after the proceed method of the chain. For example, our application interceptor prints a request log before the chain’s Proceed method, and a response log after the chain’s Proceed method retrieves the result. Each interceptor interceptor calls the chain’s proceed method to get a response from the next interceptor and return it to the last interceptor.
The logic is summarized as follows:
This is the core of the okHTTP execution flow, which is as follows:
- Interceptor chain: The original request request is passed to each interceptor in turn. The interceptor processes the response and sends it back in reverse.
- Interceptor: You can process a request, then call the index+1 interceptor chain proceed method to get the result of the next interceptor process, and then process the result yourself: Request, chain.proceed, and response.
If you haven’t noticed, this process is very similar to the production process of a company:
- The boss received an order to produce 100 computers in 10 days.
- After the general manager got the task, he revised the task and time: 110 sets should be produced within 8 days, which is based on the qualified rate of production and the time for reengineering, inspection, packaging and transportation. Both quality and quantity should be guaranteed and delivery should be made on time.
- Then the task came to the department manager, who first confirmed whether there was enough inventory in the warehouse. If there was, he would directly use the inventory to deliver the goods, so that there was no delivery risk (quality, time). If there is no stock, then go to the production line.
- After the production line is finished on time and in quantity, the production situation will be reported to the department manager, who will summarize the results in Excel and present them to the general manager, who will summarize the results of the whole production process and the cooperation of each department into PPT and report to the boss.
Different interceptors play different roles in the network request task. Maybe the author of OKHTTP was inspired to write an interceptor from life, haha.
The interceptor | role |
---|---|
Apply interceptor | Handle the original request and the final response: you can add custom headers, generic parameters, parameter encryption, gateway access, and so on. |
RetryAndFollowUpInterceptor | Handles error retries and redirects |
BridgeInterceptor | The main job of the application layer and network layer bridge interceptor is to add cookies to the request, add fixed headers, such as Host, Content-Length, content-Type, user-agent, etc., and then save the cookie of the response result. If the response has been compressed using gzip, you also need to decompress it. |
CacheInterceptor | Cache interceptor, get cache, update cache. No network request is made if the cache is hit. |
ConnectInterceptor | Connection interceptors, which internally maintain a connection pool, are responsible for connection reuse, connection creation (three-way handshake, etc.), connection release, and socket flow creation on connections. |
Network interceptor | User-defined interceptors are typically used to monitor data transfers at the network layer. |
CallServerInterceptor | Request interceptor, after the preparatory work is completed, really initiate network request, IO read and write. |
The role of each interceptor is outlined here, and the next article will look at each interceptor in detail, along with the important points of caching and connection pooling.
So that’s the end of our source code analysis of the OKHTTP execution flow.
conclusion
Now, in two articles, we have mastered the basic usage of OKHTTP and read the source code to understand the overall execution flow of OKHTTP — request creation, scheduling, and interceptor chain processing. The following articles will delve into the implementation of each interceptor and learn more advanced techniques for using okHTTP, so stay tuned for more.
.
Welcome to my husband. No. All the.