What is a OkHttp

  • The OkHTTP application network request framework, which effectively executes HTTP requests, makes content load faster and saves bandwidth. Enable developers to quickly and efficiently network request development, avoid using HttpURLConnection native request mode when the repeated coding process.

Quick use of OkHttp

A synchronous request

  1. Create the OkHttpClient and Request objects
  2. Encapsulate the Request as a Call object with the OkHttpClient object
  3. Call call.execute() to send the synchronous request
val client = OkHttpClient.Builder()
            .readTimeout(5,TimeUnit.SECONDS)
            .build()
val request = Request.Builder()
             .url("http://www.baidu.com")
             .build()
val call = client.newCall(request)
try {
    val response = call.execute()
    Log.d("OkHttp", response.body.toString())
}catch (e:IOException){
    e.printStackTrace()
}
Copy the code

An asynchronous request

  1. Create the OkHttpClient and Request objects
  2. Encapsulate the Request as a Call object with the OkHttpClient object
  3. Send the asynchronous request by calling call.enqueue() and passing in a Callback
val client = OkHttpClient.Builder().readTimeout(5,TimeUnit.SECONDS).build()
val request = Request.Builder().url("http://www.baidu.com").build()
val call = client.newCall(request)
call.enqueue(object :Callback{
    override fun onFailure(call: Call, e: IOException) {
        Log.d("OkHttp", "onFailure: ")
    }
    override fun onResponse(call: Call, response: Response) {
        Log.d("OkHttp", response.body.toString())
    }
})
Copy the code

OkHttp principle analysis

Design patterns

  • Builder
  • Chain of responsibility
  • Template method

The overall architecture

  • The okHttpClientl class is at the heart of the framework, where Dispatcher and thread pools are the core content. The Dispatcher Dispatcher and thread pools are created through Bulider, whether synchronous or asynchronous requests are made through the DispatcherdispatcherPerform scheduling and cooperate in the network request processgetResponseWithInterceptorChainMethod connector chain to work. Calling the Call method is actually calling the RealCall method.

Request source analysis synchronously

  • Before we callcall.execute()Method, in fact we can find the real implementation class in the source code is the RealCall classexecute()methods

Determine whether the same synchronous request out first, and then to event listeners callback, we found that there are two important steps at this moment, a dispatcher is the call to the dispenser the execute () method, the other is getResponseWithInterceptorChain () interceptors, We first point into the client. The dispatcher. Executed (this) is analyzed, behind the interceptor.

When we clicked on it, it turned out that the dispenser was only adding calls to the runningSyncCalls run queue when synchronizing requests

Request source analysis asynchronously

  • Or just look at itcall.enqueue()In this case, you need to pass in a Callback interface implementation class. The implementation class of call is RealCallcall.enqueue()Is actually called in the RealCallenqueue()methods

First determine whether the current call has been executed, if so, an exception will be thrown. The call defined above is executed only once, which is also related to connection pool. CaptureCallStackTrace () is the exception stack information that captures the HTTP request. Next, start listening for some events. With the client. The dispatcher (). The enqueue (new AsyncCall (responseCallback)); The most important step is to discover that the callback we pass in is encapsulated as an AsyncCall object, which is a Runnable object, This is then passed to the enqueue() method of the Dispatcher (which is created in the Builder() of OkHttpClient).

In okHttp3.9.x, the enqueue is in a synchronized block, First, determine whether the number of asynchronous requests in the runqueue is greater than maxRequests(64) and whether the number of requests per running host is smaller than maxRequestsPerHost(5). If so, add runningAsyncCalls to the runqueue and hand it to the thread pool for execution. Otherwise add to wait queue readyAsyncCalls

In okHttp4.9.0, the AsyncCall is added directly to the queue, and the AsyncCall is implemented to share the AtomicInteger of the existing run call to the same host, and the scheduling is handed over to the promoteAndExecute()

The promoteAndExecute() method iterates over readyAsyncCalls and exits the loop if the current asynchronous requests are greater than maxRequests(64). If the number of request hosts is greater than maxRequestsPerHost(5), it will break out of this loop. If neither of them is satisfied, it will take out a queue element and put it into the run queue and execution list. Finally, the method returns ture to indicate that the request is running.

After updating the corresponding queue list, run asynccall.executeon (executorService) to achieve some degree of decoupling, Pass the threaded service executorService to the executeOn() method, which is primarily (as in okHttp3.9.x)executorService.execute(this) method for the thread pool to execute, Finally execute client. The dispatcher. Finished (this) is the recursive call promoteAndExecute () until the asynchronous request waiting queue is empty

  • So let’s go back to the pitchasyncCall.executeOn(executorService)

ExecutorService is an executorService singleton pool. The first argument is passed to 0 to mean that no core threads are idle, the second argument is set to the maximum number of threads, but is also affected by maxRequests, and the third and fourth arguments are to close idle threads after 60s

Execute (this)this refers to the current AsycCall, since it is a thread pool. So the run method of each child thread is called, that is, the run method of AsyncCall is called

inAsnycCallIn version 4.9.0AsyncCallThere is less NamedRunnable than in previous Java versions (such as 3.12.1), and the runable interface is implemented directly so that the main flow is executed directly in the runHere is a part of the OKHTTP coregetResponseWithInterceptorChain()Interception chain (More on that later). The main network success request is inresponseCallback.onResponse(this@RealCall, response), which also validates the callbackonResponsewithonFailureIt’s all in the child thread. The catch block is used to handle failed requests and print logs. The final execution was importantclient.dispatcher.finished(this)Method, the important thing is that it calls the other onefinished()methodsThere are three main things you do in Finish ()

  • Removes the current request from the queue in the synchronization block
  • Then adjust the asynchronous request queue
  • Recalculate the number of queue threads executing and copy it

The second and third points are all completed in promoteAndExecute()


OkHttp Dispatcher for task dispatching

  • The dispatcher is inOkHttpClient.Builder()Created in the process
  • Diapatcher maintains simultaneous/asynchronous request status
  • Diapatcher Maintains the thread poolExcutorService, and used for executionreadyAsynCallswithrunningAsycCallsCall in queue

Why do synchronous requests use one queue and asynchronous requests need to connect to another

  • DispatcherWant to work with the producer (default in the main thread) whileExcutorServiceRelative to the consumer pool,Deque<readyAsycCall>As a cache,Deque<runningAsycCall>Is the running queue

When are threads in the readyAsycCall queue executed

  • In earlier versions of okHttp3.9.x, the current call was moved out of the dispatcher’s finsh method, the promotecalls() was called to adjust the task queue, and then the thread count was recalculated. In these new versions of 4.9.0, promoteAndExecute() is called to adjust the queue and recalculate threads after the call is removed from the finsh method of the dispenser that is finally called. The promoteAndExecute() is called when the enqueue() method executes to adjust the queue and recalculate the number of threads. It is important to note that the execution of the thread pool in the new version of OKHTTP is also in the promoteAndExecute() method.

  • Okhttp3.9.x version finish() method

  • Okhttp4.9.0 versionfinish()methods


OkHttp interceptor

  • Interceptors are a powerful OkHttp mechanism that can implement network listening, request, and corresponding rewrite, request failure rewrite, and other functions. Interceptors work asynchronously or synchronously.

Interceptors provided by OkHttp

  • These include retry and redirection interceptors, bridge interceptors, cache interceptors, connection interceptors, and request write I/O network flow interceptors

GetResponseWithInterceptorChain () method

  • Create interceptors and put them in the list (in addition to the five system interceptors provided by OKHTTP, there are application interceptors and network interceptors). As the source code is known this method puts each interceptor through the collection intoRealInterceptorChain, and then callchain.proceed()Methods.

  • inchain.proceed()The most important thing in the method is to create a chain of interceptors. The difference is that the parameters passed in here areindex+1, which means that access can only be made from the next interceptor. That’s the clever part of the design, the chain of interceptors.val response = interceptor.intercept(next)To perform the index and pass in what we just created so that our connectors form a complete chain.

  • Let’s take a lookRetryAndFollowUpInterceptorThe interceptorinterceptMethod, which still executes the next interceptorproceedSo the entire okHTTP network request is equivalent to the execution of one interceptor at a time

The subtotal

RetryAndFollowUpInterceptor interceptor

  • Mainly responsible for failed reconnection. One is created in the okHttp3.9.x releaseStreamAllocation, which provides some components for Http requests and allocates the Stream that the creation here will provide toConnectInterceptorInterceptor use. This class was not created in 4.9.x.
  • The main reconnect code is thereRetryAndFollowUpInterceptorthewhileThe next interceptor is called. The maximum reconnection for failed network requests is set toFor MAX_FOLLOW_UPS(20)

BridgeInterceptor interceptor

  • I was responsible for setting content length, encoding method, compression, adding headers, and connection reuseKeep-Alive(Maintain link status for a certain period of time)

  • The same thing is calledproeccdMethod, and return Response. The function of the current interceptor is to call the next interceptor, return Response, and return the previous interceptor.

  • cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)

The Response function is to turn the Response returned by the server into a Response that can be processed by the user

  • The next step is to determine whether the server supports itgzipCompress, make sure the corresponding headContent-EncodingsupportgzipCompression is done with HTTP headersbodyBody. thenresponseBodyThe input flow ofGzipSource“, so that the caller uses the body in the decompressed form.

BridgeInterceptor summary of interceptors

  • Convert a user-built Request into a network accessible Request
  • Put this in line with the network Request to make a network Request
  • Convert the Response returned by the server into the Response available to the user

How to use okHTTP caching

  • Add the Cache class and set the Cache directory and size

The put method in the Cache

  • There is an important class in the Cache classDiskLruCache.EditorWhich actually means that the entire core of the cache is thisLRU algorithmWhile the OKHTTP internal maintainer cleans up the thread pool and the cache is cleaned up by the thread pool.
  • Due to therequestMethodfor"GET", it returnsnullIt does not cacheGetmethods
  • An Entry is a wrapper class composed of URL, varyHeaders, requestMethod, Protocol, Code, and Message.

  • The lasteditorSave the request and response headers, thatRealCacheRequest(editor)The request body, along with the interface it implements, is savedCacheRequestIs used to expose the cache interceptor to update the cache information based on the interface.

The get method in the Cache

  • The primary user reads the response body from the cacheresponse, as can be seen from the source code, in addition to obtainkeyThere are also cached snapshots with entities throughentry.response()To obtainresponseAnd finally match request and response.

CacheInterceptor interceptor

  • First of all byCacheStrategyThe factory class of “gets” whether the caching strategy is used over the network, cache, or both.
  • cache? .trackResponse(strategy)To update the cache hit ratio of the statistical indicator
  • The next step is to determine if the cache meets the metric or turn it offcacheCandidate.body? .closeQuietly()
  • If the network is not available and there is no cache, build through the builder patternResponseIt also throws a 504 error

  • If there is a cache and the network cannot be used, return the cache result

  • The network response is fetched by calling the PROCEED method, leaving the work to the next interceptor

  • If the response code isHTTP_NOT_MODIFIED(304) is obtained directly from the cache

  • Finally, it determines whether the Http header has a response body and can be cached, and passescache.put(response)Write to the cache, and then passHttpMethod.invalidatesCache(networkRequest.method)Check if it is an invalid cache method, if it is, remove it from the cache pool.

ConnectInterceptor interceptor

The main function is to open the link between the server and the establishment of flow objects, officially open okHTTP network request.

  • In version OKHttp3.9.x, this is where you get the created from the redirection connectorStreamAllocation, which distributes the stream.
  • throughStreamAllocation.newStream()To create aHttpCodec.HttpCodecThe main functions of the coderequestAnd decodingresponse.
  • To get toRealConnectionIs the actual network I/O transmission.
  • The last callproceedMethod for interceptor operations

  • In okHttp4.x, there is noStreamAllocationClass, but the interceptor’s functionality is largely the same
  • throughrealChain.call.initExchange(chain)Find a new or pooled connection to host incoming requests and responses
  • theexchangePass in the connector chain and useproceedmethods

Connection pooling ConnectionPool

  • The function manages Connectio reuse within a time range
  • ininitExchangeMethodexchangeFinder.find(client, chain)->findHealthyConnection->findConnection-> connectionPool.put(newConnection)Method, put the createdRealConnection(connectionPool, route)In theConcurrentLinkedQueue<RealConnection>()In the queue
  • inExchangeFinderthefindHealthyConnectionMethod by callingconnectionPool.callAcquirePooledConnectionMethod tries to get a reclaim connection from [address] for [call]. Returns true if a connection was obtained

  • In the RealConnectionPoolcleanupMade some GC collection algorithms to ensure multiple healthy keep-alie connections

CallServerInterceptor interceptor

  • Make a real network request and receive the response the server sends back to us
  • exchange.writeRequestHeaders(request)Writes the header information of the request

  • requestBody.writeTo(bufferedRequestBody)Write the body information we requested

! [wechat screenshot _20210727015527.png](p6-juejin.byteimg.com/tos-cn-i-k3… plv-k3u1fbpfcp-watermark.image)

  • Next, finish writing the network request information

  • The header of the response is then read

  • The response body information is then read in the non-special case

  • The last callexchange.noNewExchangesOnConnection()Prevents further exchanges from being created on this connection and throws exceptions in cases 204 or 205

CallServerInterceptor Summary of the interceptor

  • Write the request header
  • Write the body information of the request
  • End The entire network request write is complete
  • Read the Response header
  • Read the body information of response

The general process of a network request in OKHTTP

  • Request is encapsulated into a Call object through okhttpClient for the same/different requests
  • Dispatcher Request distribution from the dispatcher
  • The interceptor chain getRsponseWithInterceptors ()