About OkHttp
OkHttp is an HTTP+HTTP/2 client for Android and Java applications.
How to use
Just add rely on Android development, are as follows: implementation (” com. Squareup. Okhttp3: okhttp: 3.13.1 “)
Official example 1: Get the content of a URL and print it
/ / Http client
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
// Construct the request
Request request = new Request.Builder()
.url(url)
.build();
// Execute the request to get the data
try (Response response = client.newCall(request).execute()) {
returnresponse.body().string(); }}Copy the code
Official Example 2: Post data to the server
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
returnresponse.body().string(); }}Copy the code
A quick look at the code: The POST request operation looks simple. But we need to learn a few important interfaces:
OKHttpClient: it represents the HTTP client. Request: it encapsulates the Request object. You can construct an HTTP Request object. The client.newCall() Call generates a request execution object called, which encapsulates the request execution process.
Here we combine this example to analyze the source code:
newCall().execute()
After tracing the source code, we found that this method is the interface in Call, with the following code:
/** * A call is a request that has been prepared for execution. A call can be canceled. As this object * represents a single request/response pair (stream), it cannot be executed twice. */
public interface Call extends Cloneable {
// Omit some code
// Execute requests synchronously
Response execute(a) throws IOException;
// Request to join the queue
void enqueue(Callback responseCallback);
// Omit some code
}
Copy the code
The Call implementation class is RealCall. Follow the source realcall. Java file and you can see the execute method:
@Override public Response execute(a) throws IOException {
// The synchronization lock checks whether the request has been executed and if not, the flag executed = ture or an exception is thrown
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
// The callback method callStart is called
eventListener.callStart(this);
try {
// The okHTTP client calls the Dispatcher to execute the request object
client.dispatcher().executed(this);
/ / call the getResponseWithInterceptorChain method to obtain the Response to the Response data, later will continue to analysis
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
// Failed to request callback callFailed
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
// Call the finished method using the dispather object to complete the request}}Copy the code
Next, we detailed analyze the dispatcher. The execute and getResponseWithInterceptorChain these two methods:
Trace source Dispatcher
public final class Dispatcher {
// Omit some code
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if(! call.get().forWebSocket) { AsyncCall existingCall = findExistingCallWithHost(call.host());if(existingCall ! =null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
if(! calls.remove(call))throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if(! isRunning && idleCallback ! =null) { idleCallback.run(); }}}Copy the code
Discover that the Dispatcher is a scheduler whose purpose is to distribute requests. It has three internal queues, namely synchronous request queue, asynchronous request waiting queue, asynchronous request execution queue.
The core method: getResponseWithInterceptorChain
Response getResponseWithInterceptorChain(a) throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());// Add a custom interceptor
interceptors.add(retryAndFollowUpInterceptor);// Retry the redirection interceptor
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if(! forWebSocket) { interceptors.addAll(client.networkInterceptors()); } interceptors.add(new CallServerInterceptor(forWebSocket));// Invoke server interception
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null.null.null.0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
// Start responsibility chain mode
return chain.proceed(originalRequest);
}
Copy the code
After building several interceptors, this approach constructs an interceptor responsibility chain. RealInterceptorChain:
RealInterceptorChain
This class is responsible for concatenating all interceptors so that all interceptors are implemented recursively, ensuring that a Response is returned only after all interceptors have been executed. The following explains the main method in this class, proceed:
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
// Count the number of times the interceptor calls the method
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");
}
// Generate the next interceptor to invoke the method's RealInterceptorChain object, where index+1 is used to get the next interceptor
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
// Get the current interceptor
Interceptor interceptor = interceptors.get(index);
// Call the interceptor and get the return result
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");
}
return response;
}
Copy the code
conclusion
That’s our analysis of OkHttp’s core source code. A Call instance is initialized when a request is made, and its execute() and enqueue() methods are called, depending on whether the Call is synchronous or asynchronous. The general process is to retrieve a response and deliver it to the user through a chain of interceptors, retry, bridge, cache, connect, and access the server.