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
- Create the OkHttpClient and Request objects
- Encapsulate the Request as a Call object with the OkHttpClient object
- 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
- Create the OkHttpClient and Request objects
- Encapsulate the Request as a Call object with the OkHttpClient object
- 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 Dispatcher
dispatcher
Perform scheduling and cooperate in the network request processgetResponseWithInterceptorChain
Method connector chain to work. Calling the Call method is actually calling the RealCall method.
Request source analysis synchronously
- Before we call
call.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 it
call.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 pitch
asyncCall.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
inAsnycCall
In version 4.9.0AsyncCall
There 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 callbackonResponse
withonFailure
It’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 in
OkHttpClient.Builder()
Created in the process - Diapatcher maintains simultaneous/asynchronous request status
- Diapatcher Maintains the thread pool
ExcutorService
, and used for executionreadyAsynCalls
withrunningAsycCalls
Call in queue
Why do synchronous requests use one queue and asynchronous requests need to connect to another
Dispatcher
Want to work with the producer (default in the main thread) whileExcutorService
Relative 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 version
finish()
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 into
RealInterceptorChain
, and then callchain.proceed()
Methods.
- in
chain.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 look
RetryAndFollowUpInterceptor
The interceptorintercept
Method, which still executes the next interceptorproceed
So 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 release
StreamAllocation
, which provides some components for Http requests and allocates the Stream that the creation here will provide toConnectInterceptor
Interceptor use. This class was not created in 4.9.x. - The main reconnect code is there
RetryAndFollowUpInterceptor
thewhile
The 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 reuse
Keep-Alive
(Maintain link status for a certain period of time)
- The same thing is called
proeccd
Method, 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 it
gzip
Compress, make sure the corresponding headContent-Encoding
supportgzip
Compression is done with HTTP headersbody
Body. thenresponseBody
The 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 class
DiskLruCache.Editor
Which actually means that the entire core of the cache is thisLRU algorithm
While the OKHTTP internal maintainer cleans up the thread pool and the cache is cleaned up by the thread pool. - Due to the
requestMethod
for"GET"
, it returnsnull
It does not cacheGet
methods - An Entry is a wrapper class composed of URL, varyHeaders, requestMethod, Protocol, Code, and Message.
- The last
editor
Save the request and response headers, thatRealCacheRequest(editor)
The request body, along with the interface it implements, is savedCacheRequest
Is 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 cache
response
, as can be seen from the source code, in addition to obtainkey
There are also cached snapshots with entities throughentry.response()
To obtainresponse
And finally match request and response.
CacheInterceptor interceptor
- First of all by
CacheStrategy
The 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 off
cacheCandidate.body? .closeQuietly()
- If the network is not available and there is no cache, build through the builder pattern
Response
It 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 is
HTTP_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 passes
cache.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 connector
StreamAllocation
, which distributes the stream. - through
StreamAllocation.newStream()
To create aHttpCodec
.HttpCodec
The main functions of the coderequest
And decodingresponse
. - To get to
RealConnection
Is the actual network I/O transmission. - The last call
proceed
Method for interceptor operations
- In okHttp4.x, there is no
StreamAllocation
Class, but the interceptor’s functionality is largely the same - through
realChain.call.initExchange(chain)
Find a new or pooled connection to host incoming requests and responses - the
exchange
Pass in the connector chain and useproceed
methods
Connection pooling ConnectionPool
- The function manages Connectio reuse within a time range
- in
initExchange
MethodexchangeFinder.find(client, chain)
->findHealthyConnection
->findConnection
->connectionPool.put(newConnection)
Method, put the createdRealConnection(connectionPool, route)
In theConcurrentLinkedQueue<RealConnection>()
In the queue - in
ExchangeFinder
thefindHealthyConnection
Method by callingconnectionPool.callAcquirePooledConnection
Method tries to get a reclaim connection from [address] for [call]. Returns true if a connection was obtained
- In the RealConnectionPool
cleanup
Made 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 call
exchange.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 ()