OkHttp – official address OkHttp – GitHub code address

summary

This article mainly starts with two OkHttp request examples, and introduces the source code analysis of OkHttp initialization and the process from construction, distribution and execution of the request

OkHttp overall flow (covered in red)

This article covers code flowcharts

The sample

Using the OkHttp general flow, initialize a shared OkHttpClient, build a Request, then OkHttpClient builds a Call from the Request, then executes a Call, and finally performs a Response

A synchronous request

public class GetExample { OkHttpClient client = new OkHttpClient(); String Run (String URL) throws IOException {Request Request = new request.Builder () // Build the Request .url(url) .build(); Try (Response Response = client.newCall(request).execute()) {return response.body().string();
    }
  }

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md"); System.out.println(response); }}Copy the code

An asynchronous request

public final class AsynchronousGet { private final OkHttpClient client = new OkHttpClient(); Client public void run() throws Exception {// Build Request Request Request = new request.builder ().url("http://publicobject.com/helloworld.txt") .build(); NewCall (Request).enqueue(new).newCall(request).enqueue(newCallback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
        try (ResponseBody responseBody = response.body()) {
          if(! response.isSuccessful()) throw new IOException("Unexpected code " + response);

          Headers responseHeaders = response.headers();
          for (int i = 0, size = responseHeaders.size(); i < size; i++) {
            System.out.println(responseHeaders.name(i) + ":"+ responseHeaders.value(i)); } System.out.println(responseBody.string()); }}}); } public static void main(String... args) throws Exception { new AsynchronousGet().run(); }}Copy the code

Source code analysis

Build OkHttpClient

  • OkHttpClient is a factory class for Call, and OkHttpClient should be shared, or singleton
  • Clients can be customized using newBuilder
  • There is no need to worry about shutting down and freeing resources
/*
 * Factory for [calls][Call], which can be used to send HTTP requests and read their responses.
 * ## OkHttpClients Should Be Shared
 * ## Customize Your Client With newBuilder()
 * ## Shutdown Isn't Necessary* / open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory { // 3. Member variable initialization... Constructor () : this(); // 4. The OkHttpClient function initializes init {// Initializes the certificate and interceptor. } // 2. Build a class Builderconstructor() {... }}Copy the code

OkHttpClient.Builder

Builder mode, provides the ability to customize configuration, with a default configuration that you don’t need to care about

  class Builder constructor() {
        internal var dispatcher: Dispatcher = Dispatcher()
        internal var connectionPool: ConnectionPool = ConnectionPool()
        internal val interceptors: MutableList<Interceptor> = mutableListOf()
        internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
        internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
        internal var retryOnConnectionFailure = true
        internal var authenticator: Authenticator = Authenticator.NONE
        internal var followRedirects = true
        internal var followSslRedirects = true
        internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
        internal var cache: Cache? = null
        internal var dns: Dns = Dns.SYSTEM
        internal var proxy: Proxy? = null
        internal var proxySelector: ProxySelector? = null
        internal var proxyAuthenticator: Authenticator = Authenticator.NONE
        internal var socketFactory: SocketFactory = SocketFactory.getDefault()
        internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
        internal var x509TrustManagerOrNull: X509TrustManager? = null
        internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
        internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
        internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
        internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
        internal var certificateChainCleaner: CertificateChainCleaner? = null
        internal var callTimeout = 0
        internal var connectTimeout = 10_000
        internal var readTimeout = 10_000
        internal var writeTimeout = 10_000
        internal var pingInterval = 0
    }   

Copy the code

OkHttpClient member variable is initialized

  • Initialize a member variable
  • JvmName is intended to support compatibility with 3.x
  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

  @get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool

  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()

  @get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
      builder.networkInterceptors.toImmutableList()

  @get:JvmName("eventListenerFactory") val eventListenerFactory: EventListener.Factory =
      builder.eventListenerFactory

  @get:JvmName("retryOnConnectionFailure") val retryOnConnectionFailure: Boolean =
      builder.retryOnConnectionFailure

  @get:JvmName("cookieJar") val cookieJar: CookieJar = builder.cookieJar

  @get:JvmName("cache") val cache: Cache? = builder.cache

  ....
Copy the code

Build Request

Request corresponds to the Request in the HTTP Request. OkHttp is still the Builder construction mode to build the Request

Request.Builder

Supports the CONFIGURATION of URL, Method, headers, and body

open class Builder { internal var url: HttpUrl? = null internal var method: String internal var headers: Headers.Builder internal var body: RequestBody? Request open fun build(): Request {return Request(
          checkNotNull(url) { "url == null" },
          method,
          headers.build(),
          body,
          tags.toImmutableMap()
      )
    }
}
Copy the code

Request

Construct the Request instance from request. Builder

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody? , internal val tags: Map<Class<*>, Any> ) { ... }Copy the code

Build the Call

OkHttpClient.newCall

OkHttpClient implements call.Factory as a constructor Factory class for Call

Override Fun newCall(Request: request): Call {// Executes the constructor Call method of RealCallreturn RealCall.newRealCall(this, request, forWebSocket = false)}Copy the code

RealCall.newRealCall

Construct the Call real method, and create an emitter. Let’s talk about Call

companion object {
    fun newRealCall(
      client: OkHttpClient,
      originalRequest: Request,
      forWebSocket: Boolean
    ): RealCall {
      
      return RealCall(client, originalRequest, forWebSocket). Apply {// create a transmitter, which is the bridge between the application layer and the network layer, which will be introduced in the future = transmitter (client, this)}}}Copy the code

RealCall

Call is defined as a request that is ready to be executed, can be cancelled, and can only be executed once (HTTP request is also executed once), including a core member variable called Transmitter, two important methods execute (synchronous) and enQueue (asynchronous)


internal class RealCall private constructor(
  val client: OkHttpClient,
  /** The application's original request unadulterated by redirects or auth headers. */ val originalRequest: Request, val forWebSocket: Boolean): Call {// transmitter private lateinit var transmitter: transmitter override fun execute(): Response { ... } override fun enqueue(responseCallback: Callback) {... } // Cancel fun cancel() {transmitter. Cancel ()}Copy the code

A synchronous request

RealCall.execute

  • Pre-request validation logic, which can only be performed once, and expiration time logic to determine logic
  • Notification request start event for metrics data collection
  • Adds the call to the dispatcher’s synchronous request queue
  • Requests and returns a series of logical processes through the interceptor chain of responsibility pattern
Override fun execute(): Response {synchronized(this) {check(! executed) {"Already Executed" }
      executed = trueTimeoutEnter () // Notify start, and finally send the time through EventListener. CallStart () try {// Call the client's Dispatcher to execute the call (add the call to the synchronous call queue) Client. The dispatcher. Executed (this) / / through the interceptor chain of responsibility pattern request and returns processing such as a series of logicreturn getResponseWithInterceptorChain()
    } finally {
      client.dispatcher.finished(this)
    }
  }
Copy the code

RealCall.getResponseWithInterceptorChain

  • Configure interceptors so that all request and response processing logic is decoupled to each interceptor responsible module
  • Construct the RealInterceptorChain instance of the interceptor chain invocation class
  • Chain.proceed makes a chain call to the interceptor
fun getResponseWithInterceptorChain(): Val interceptors = mutableListOf<Interceptor>() interceptors += client.interceptors // Client configuration of interceptor interceptors + = RetryAndFollowUpInterceptor (client) / / retry mechanism interceptor interceptors + = BridgeInterceptor(client.cookiejar) // Request and return bridge (HTTP information configuration and parsing) interceptors += CacheInterceptor(client.cache) // CacheInterceptor Interceptors += ConnectInterceptor // ConnectInterceptorif (!forWebSocket) {
      interceptors += client.networkInterceptors
    }
    interceptors += CallServerInterceptor(forVal Chain = RealInterceptorChain(interceptors, transmitter, NULL, 0, originalRequest) this, client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis) var calledNoMoreExchanges =falseVal response = chain.proceed(originalRequest)if(transmitter. IsCanceled) {// Response. CloseQuietly () throw IOException("Canceled")}returnResponse // Return request result} catch (e: IOException) {calledNoMoreExchanges =true
      throw transmitter.noMoreExchanges(e) as Throwable
    } finally {
      if(! calledNoMoreExchanges) { transmitter.noMoreExchanges(null) } } }Copy the code

An asynchronous request

RealCall.enqueue

  • Construct an asynchronous call
  • Call dispatcher to enqueue AsyncCall
override fun enqueue(responseCallback: Callback) { synchronized(this) { check(! executed) {"Already Executed" }
      executed = true} transmitter. CallStart AsyncCall () / / construction, then the dispenser team operation client. The dispatcher. The enqueue (AsyncCall (responseCallback)}Copy the code

Dispatcher Dispatcher Dispatcher Dispatcher Call for distribution execution

Reference: Thread pool understanding

  • At initialization, the thread pool for the dispenser is constructed, along with the corresponding execution parameters
  • Mainly manage the processing of asynchronous requests (asynchronous Call queuing, concurrent execution)
class Dispatcher constructor@get:Synchronized var maxRequests = 64 @get:Synchronized var maxRequestsPerHost = 5 // The thread pool executor creates a cacheable thread pool by default."executorService") val executorService: ExecutorService
    get() {
      if (executorServiceOrNull == null) {
        executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
            SynchronousQueue(), threadFactory("OkHttp Dispatcher".false))}returnexecutorServiceOrNull!! } /** readyAsyncCalls = ArrayDeque<AsyncCall>() /** readyAsyncCalls */ private val RunningAsyncCalls = ArrayDeque<AsyncCall>() /** Synchronous call pairs */ private Val runningSyncCalls = ArrayDeque<RealCall>() // Internal fun enqueue(Call: AsyncCall) {synchronized(this) {readyAsynccalls.add (call) // same host case multiplexing logic processingif(! call.get().forWebSocket) { val existingCall = findExistingCallWithHost(call.host())if(existingCall ! = null) call. ReuseCallsPerHostFrom (existingCall)}} promoteAndExecute () / / thread pool actuators, } // Call Private Fun promoteAndExecute(): Boolean { this.assertThreadDoesntHoldLock() val executableCalls = mutableListOf<AsyncCall>() val isRunning: Boolean synchronized(this) {readyasynccalls.iterator () Boolean synchronized(this) {readyasynccalls.iterator ()while (i.hasNext()) {
        val asyncCall = i.next()

        if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
        if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue// Host Max capacity.i.rove () asynccall.callsperhost ().incrementandget () executableCalls Runningasynccalls.add (asyncCall) // Join the running queue} isRunning = runningCallsCount() > 0}for (i in0 until executableCalls. Size) {val asyncCall = executableCalls[I] asynccall. executeOn(executorService) // Execute in Call}returnSynchronized internal fun executed(call: RealCall) {runningSynccalls.add (call)}}Copy the code

AsyncCall perform

  • ExecuteOn, thread pool executor executes Runnable
  • Run, through the interceptor to request and get the results of the response
fun executeOn(executorService: ExecutorService) {
    client.dispatcher.assertThreadDoesntHoldLock()
    
    var success = falseExecutorservice. execute(this) success =true}} // Execute method override funrun() {
    threadName("OkHttp ${redactedUrl()}") {
        var signalledCallback = falseTransmitter. TimeoutEnter (try) {/ / with "synchronous request" in the interceptor chain call val response = getResponseWithInterceptorChain () signalledCallback =true/ / response callback responseCallback. OnResponse (this @ RealCall, response)} the catch (e: IOException) {... } finally { client.dispatcher.finished(this) } } }Copy the code

OkHttp 4 source code (2) – interceptor mechanism analysis