Okhttp who is it? Do what?

An open source project that handles web requests and is the most popular lightweight framework for Android, Use to replace HttpUrlConnection and Apache HttpClient(Android API23 6.0)

Function:

Get, POST Request file upload, download, load image (internal image size automatically compressed) support request callback, directly return objects, object collection support session hold

Advantages:

Allow all requests to connect to the same host address, improve request efficiency share Socket, reduce the number of requests to the server through the connection pool, reduce request latency cache response data to reduce repeated network requests reduce the consumption of data traffic automatic processing GZip compression googleplay recommended network framework

This article will analyze the source code and simple usage of OkHttp

First, basic use

A synchronous request

// (1) create OkHttpClient object OkHttpClient = new OkHttpClient(); Request Request = new Request.builder ().url(url).build(); // (3) Create a Call object. Call call = client.newCall(request); // (4) send the request and get the data returned by the server Response Response = call.execute(); String data = Response.body ().string();Copy the code

An asynchronous request

// (1) create OkHttpClient object OkHttpClient = new OkHttpClient(); Request Request = new Request.builder ().url(url).build(); // (3) Create a Call object. Call call = client.newCall(request); Call.enqueue (new Callback() {@override public void onFailure(call call); IOException e) { } @Override public void onResponse(Call call, Response Response) throws IOException {// (5) Retrieve the corresponding data. String data = Response.body ().string(); }});Copy the code

Second, source code analysis

Simple figure

2.1 OkHttpClient object

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; 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; 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; if (interceptors.contains(null)) { throw new IllegalStateException("Null interceptor: " + interceptors); } if (networkInterceptors.contains(null)) { throw new IllegalStateException("Null network interceptor: " + networkInterceptors); }}Copy the code

New OkHttpClient() uses constructor mode to initialize some configuration information: support protocol, task dispatcher (which contains a pool of threads internally to perform asynchronous requests), connection pool (which contains a pool of threads internally to maintain connections), connection/read/write timeout duration, and so on.

public Builder() { dispatcher = new Dispatcher(); // Dispenser protocols = DEFAULT_PROTOCOLS; // HTTP protocol connectionSpecs = DEFAULT_CONNECTION_SPECS; // Transport layer version and connection protocol eventListenerFactory = eventListener.factory (eventListener.none); // Event listener factory proxySelector = proxyseler.getDefault (); CookieJar = cookiejar.no_cookies; // cookie socketFactory = SocketFactory.getDefault(); / / socket factory hostnameVerifier = OkHostnameVerifier. INSTANCE; / / host name that certificatePinner = certificatePinner. DEFAULT; ProxyAuthenticator = Authenticator.NONE; Authenticator = authenticator.none; // Source server authentication connectionPool = new connectionPool (); // Connection pool DNS = dns.system; // domain name followSslRedirects = true; // Directs = true; RetryOnConnectionFailure = true; // Whether to retry when the connection fails connectTimeout = 10_000; // Connection timeout readTimeout = 10_000; WriteTimeout = 10_000; PingInterval = 0; // The interval between HTTP / 2 and Web socket ping}Copy the code

2.2 the Request object

Request request = new Request.Builder() .url(url) .build(); /*Request*/ //... final HttpUrl url; final String method; final Headers headers; final @Nullable RequestBody body; final Map<Class<? >, Object> tags; / /...Copy the code

Every network Request is a Request. Request is the encapsulation of URL,method,header,body, and Request line, Request header, and entity content in THE Http protocol

Usually we build a Request object with a folding pattern to set the Request url, Request method, headers, body, and tag that can be used to cancel the Request.

2.3 the Call object

Call call = client.newCall(request);
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }
  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.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
Copy the code

RealCall is the implementation class of Call. Call defines request-related operations, such as synchronous asynchrony, cancel request, and other methods. So subsequent request-related operations are basically calling the methods defined by Call, and the actual execution of these methods is its implementation class RealCall

2.4 Requesting Data

The entire preview of the request

The synchronization request code is as follows:

@Override public Response execute() throws IOException { synchronized (this) { if (executed) throw new IllegalStateException("Already Executed"); executed = true; } captureCallStackTrace(); eventListener.callStart(this); try { client.dispatcher().executed(this); // (1) Response result = getResponseWithInterceptorChain(); // (2) if (result == null) throw new IOException("Canceled"); return result; } catch (IOException e) { eventListener.callFailed(this, e); throw e; } finally { client.dispatcher().finished(this); // (3) } } /*Dispatcher*/ synchronized void executed(RealCall call) { runningSyncCalls.add(call); } // RealCall.java Response getResponseWithInterceptorChain() throws IOException { // Build a full stack of List<Interceptor> Interceptors = new ArrayList<>(); // Apply the interceptor interceptors.addall (client.interceptors()); interceptors.add(retryAndFollowUpInterceptor); interceptors.add(new BridgeInterceptor(client.cookieJar())); interceptors.add(new CacheInterceptor(client.internalCache())); interceptors.add(new ConnectInterceptor(client)); if (! ForWebSocket) {/ / network interceptor interceptors. AddAll (client.net workInterceptors ()); } interceptors.add(new CallServerInterceptor(forWebSocket)); }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} originalRequest, this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); return chain.proceed(originalRequest); }Copy the code

(1) according to the above source we can know getResponseWithInterceptorChain () returns the Response, so access to the network to get the data from the server operation are getResponseWithInterceptorChain ()

② The interceptor chain is introduced here. The Request needs to be processed through the interceptor link before it is sent to the server and the response returned by the server

Order of interceptor execution

  • Application interceptors: Interceptors added by developers

  • Reconnection operation retryAndFollowUpInterceptor: responsible for the failure, and redirect, if the call is cancelled throws IOException

  • BridgeInterceptor: As a bridge between the network layer and the application layer, the (application layer request)user request into (network layer request)network request, and then send network request to the server, Network reseponse (network layer response) is converted into user response (application layer response)

  • CacheInterceptor: Handles requests in the cache and writes responses to the cache

  • ConnectInterceptor: Is responsible for establishing a connection with the target server

  • Network blocker: The blocker added by the developer

  • CallServerInterceptor: The last interceptor in the interceptor chain, which is responsible for sending the request to the server and getting the response data from the server

The asynchronous request code is as follows

// realcall.java.override public void enqueue(Callback responseCallback) {synchronized (this) {// if this call has already been executed, If (executed) throw new IllegalStateException("Already executed "); executed = true; } captureCallStackTrace(); eventListener.callStart(this); client.dispatcher().enqueue(new AsyncCall(responseCallback)); } AsyncCall call = new AsyncCall(responseCallback); Dispatcher dispatcher = client.dispatcher(); dispatcher.enqueue(call);Copy the code

Encapsulate responseCallback as AsyncCall

Enqueue () returns an asynchronous execution call from Dispatcher to the task Dispatcher

Solution to the Dispatcher

Public final class Dispatcher {private int requests = 64; private int requests = 64; Private int maxRequestsPerHost = 5; private int maxRequestsPerHost = 5; private @Nullable Runnable idleCallback; /** Executes calls.created Lazily. */ /** The thread pool used to execute the request and is lazily loaded */ private @nullable ExecutorService Executes orService; /** Ready async calls in the order they'll be run. */ /** Wait for asynchronous requests to be executed */ Private final Deque<AsyncCall> readyAsyncCalls =  new ArrayDeque<>(); /** 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 synchronized ExecutorService () {if (ExecutorService == null) {// The number of core threads is 0, The maximum number of threads is Integer.MAX_VALUE, ExecutorService = new ThreadPoolExecutor(0, integer.max_value, 60, timeUnit.seconds, new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false)); } return executorService; } synchronized void enqueue(AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < MaxRequestsPerHost) {// If the number of asynchronous requests in progress has not reached its maximum value // put the runningAsyncCalls flag runningAsyncCalls.add(call); // Execute the asynchronous request with executorService().execute(call); } else {// If the number of asynchronous requests in progress reaches its maximum value // put readyAsyncCalls in the readyAsyncCalls.add(call); }}... }Copy the code

The Dispatcher is a task scheduler that internally creates a thread pool, ExecutorService, and maintains three sets:

  • ReadyAsyncCalls: Collection of asynchronous requests waiting to be executed

  • RunningAsyncCalls: A collection of asynchronous requests that are being executed, including requests that have been canceled but are not completed

  • RunningSyncCalls: Set of synchronous requests that are being executed, including requests that have been canceled but are not completed

All asynchronous requests are performed by the thread pool ExecutorService.

The maximum number of threads is Integer.MAX_VALUE, and the maximum lifetime of idle threads is 60 seconds. That is, the idle threads are destroyed after 60 seconds. The maximum number of requests (synchronous and asynchronous) that can be executed is 64, and the maximum number of requests that can be executed on the same host is 5.

The thread pool calls the thread to execute AsyncCall.

// RealCall.java final class AsyncCall extends NamedRunnable { ... @Override protected void execute() { boolean signalledCallback = false; Try {/ / from the server Response is obtained by the interceptor chain Response Response Response = getResponseWithInterceptorChain (); / / if retryAndFollowUpInterceptor. Cancel () is called a so abnormal if (retryAndFollowUpInterceptor. IsCanceled ()) {signalledCallback = true; / / tag callback callback function has been invoked responseCallback. OnFailure (RealCall. This new IOException (" Canceled ")); } else {// Get the response successfully. SignalledCallback = true; / / tag callback callback function has been invoked responseCallback. OnResponse (RealCall. This response). } } catch (IOException e) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e); } else { eventListener.callFailed(RealCall.this, e); responseCallback.onFailure(RealCall.this, e); Client.dispatcher ().finished(this); client.dispatcher().finished(this); client.dispatcher().finished(this); }}}Copy the code

AsyncCall the execute () logic is very simple, getResponseWithInterceptorChain () we have to know in the last article, After getting the Response, we only need to determine whether to call onFailure() or onResponse() in responseCallback, so the callback method in enqueue() is called in the child thread, Finished () is called to notify the Dispatcher that the task has completed and that it needs to be removed from runningAsyncCalls.

The Dispatcher’s judgment is in the finish(call) that is called at the end of each asynchronous task

// dispatcher.java void Finished (AsyncCall Call) {finished(runningAsyncCalls, call, true); Void Finished (RealCall Call) {finished(runningSyncCalls, call, false); } private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { if (! calls.remove(call)) throw new AssertionError("Call wasn't in-flight!" ); If (promoteCalls) promoteCalls(); // If (promoteCalls) promoteCalls(); runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } if (runningCallsCount == 0 && idleCallback ! = null) { idleCallback.run(); }} public synchronized int runningCallsCount() {return runningAsyncCalls.size() + runningSyncCalls.size(); } private void promoteCalls() { // 1. Return if (runningAsyncCalls.size() >= maxRequests) return if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. // 2. Return if (readyAsyncCalls.isEmpty()) return; if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. // 3. Iterator<AsyncCall> I = readyAsyncCalls.iterator(); i.hasNext(); ) {// get the next waiting asynchronous task from the iterator AsyncCall Call = i.ext (); If (runningCallsForHost(call) < maxRequestsPerHost) {// Delete call i.emove () from readyAsyncCalls; // Delete call i.emove () from readyAsyncCalls; // Add call to runningAsyncCalls runningAsyncCalls.add(call); // Execute call executorService().execute(call) using the thread pool; If (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. } }Copy the code

Both synchronous and asynchronous requests are eventually called dispatcher.finish() after execution. The difference between internal execution is that asynchronous requests are called promoteCalls() to schedule readyAsyncCalls and runningAsyncCalls

2.5 the interceptor

The interceptor code is as follows:

RealCall / * * / Response getResponseWithInterceptorChain () throws IOException {/ / create a blocker collection List < Interceptor > interceptors  = new ArrayList<>(); // Add a custom interceptors.addall (client.interceptors()); / / add a retry and redirect the interceptor interceptors. Add (retryAndFollowUpInterceptor); // Add the interceptors. Add (new BridgeInterceptor(client.cookieJar())); // Add an interceptor.add (new CacheInterceptor(client.internalCache())); // Add the interceptors. Add (new ConnectInterceptor(client)); if (! ForWebSocket) {/ / add user custom network interceptor interceptors. AddAll (client.net workInterceptors ()); } // Add an interceptor for server requests: interceptors. Add (new CallServerInterceptor(forWebSocket)); // (1) Interceptor.Chain Chain = new RealInterceptorChain(interceptors, null, null, null, 0, originalRequest, null); this, eventListener, client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); // (2) handle the interceptor in the chain of responsibility return chance.proceed (originalRequest); }Copy the code

As you can see, a number of interceptors are used here, built into a chain of responsibility, and then dealt with one by one. The chain of responsibility pattern is used here, each interceptor is responsible for the corresponding function, the last interceptor will be passed to the next interceptor, until the last interceptor is completed, and then the Response layer up.

In fact, the knowledge of interceptors is very complicated, and there will be a separate article on interceptors in the future.

conclusion

After looking at the source code, I found that OkHttp is a very well designed framework. The framework uses a number of design patterns, such as the builder pattern, the chain of responsibility pattern, and so on. We know that the core of OkHttp is interceptors, here is the chain of responsibility mode, each interceptor is responsible for the corresponding function, when the request is launched from the top to the bottom of the execution of each interceptor, the response data is passed layer upon layer.

For more on Android development, please subscribe. Thanks

Finished, comments with questions

Public number [Android development programming]