okhttp 整体架构 RealCall OkHttpClient RealInterceptorChain index: Int interceptors: List<Interceptor> Response proceed(Request r) Interceptor ExchangeFinder RealConnectionPool Exchange RetryAndFollowUpInterceptor BridgeInterceptor CacheInterceptor ConnectInterceptor CallServerInterceptor 1 n
RealCall RealCall RealInterceptorChain execute getResponseWithInterceptorChain 创建Interceptor List new RealInterceptorChain proceed next = copy(index+1) 复制当前责任链,指针指向下一个拦截器 intercept(next) 执行拦截器
okhttp的拦截器分为应用拦截器 和网络拦截器 ,拦截器按照顺序执行,看下RealCall.getResponseWithInterceptorChain中拦截器的顺序定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 internal fun getResponseWithInterceptorChain () : Response { val interceptors = mutableListOf<Interceptor>() interceptors += client.interceptors interceptors += RetryAndFollowUpInterceptor(client) interceptors += BridgeInterceptor(client.cookieJar) interceptors += CacheInterceptor(client.cache) interceptors += ConnectInterceptor if (!forWebSocket) { interceptors += client.networkInterceptors } interceptors += CallServerInterceptor(forWebSocket) val chain = RealInterceptorChain( call = this , interceptors = interceptors, index = 0 , exchange = null , request = originalRequest, connectTimeoutMillis = client.connectTimeoutMillis, readTimeoutMillis = client.readTimeoutMillis, writeTimeoutMillis = client.writeTimeoutMillis )
拦截器的处理时序 这个是简化版的连接器处理时序,连接器调用到下一个都是要经过RealInterceptorChain.proceed
,这里面逻辑比较简单,就是找到下一个连接器并执行,这个细节时序图中没有体现。
应用拦截器 应用拦截器 重试重定向拦截器 重试重定向拦截器 桥接拦截器 桥接拦截器 缓存拦截器 缓存拦截器 连接拦截器 连接拦截器 网络拦截器 网络拦截器 服务请求拦截器 服务请求拦截器 intercept 初始化 ExchangeFinder 重试和重定向场景运行多次 处理请求头, 包括加载cookie 加载本地缓存 缓存池获取可用连 接或新建连接, 创建Exchange和 HTTP解码器 发送请求 解析响应 保存缓存 保存cookie gzip解压 处理异常和 返回码决定 是否重试、 重定向
同步和异步请求源码解析 okhttp异步请求调用 异步请求需要调用enqueue,如下:
1 2 3 4 5 6 7 8 9 10 11 12 private val client = OkHttpClient()fun run () { val request = Request.Builder() .url("http://publicobject.com/helloworld.txt" ) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure (call: Call , e: IOException ) {} override fun onResponse (call: Call , response: Response ) {} }) }
异步请求源码解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 private val readyAsyncCalls = ArrayDeque<AsyncCall>()private val runningAsyncCalls = ArrayDeque<AsyncCall>()internal fun enqueue (call: AsyncCall ) { synchronized(this ) { readyAsyncCalls.add(call) if (!call.call.forWebSocket) { val existingCall = findExistingCallWithHost(call.host) if (existingCall != null ) call.reuseCallsPerHostFrom(existingCall) } } promoteAndExecute() } private fun promoteAndExecute () : Boolean { this .assertThreadDoesntHoldLock() val executableCalls = mutableListOf<AsyncCall>() val isRunning: Boolean synchronized(this ) { val i = readyAsyncCalls.iterator() while (i.hasNext()) { val asyncCall = i.next() if (runningAsyncCalls.size >= this .maxRequests) break if (asyncCall.callsPerHost.get () >= this .maxRequestsPerHost) continue i.remove() asyncCall.callsPerHost.incrementAndGet() executableCalls.add(asyncCall) runningAsyncCalls.add(asyncCall) } isRunning = runningCallsCount() > 0 } for (i in 0 until executableCalls.size) { val asyncCall = executableCalls[i] asyncCall.executeOn(executorService) } return isRunning } internal fun finished (call: AsyncCall ) { call.callsPerHost.decrementAndGet() finished(runningAsyncCalls, call) } private fun <T> finished (calls: Deque <T >, call: T ) { val idleCallback: Runnable? synchronized(this ) { if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!" ) idleCallback = this .idleCallback } val isRunning = promoteAndExecute() if (!isRunning && idleCallback != null ) { idleCallback.run() } }
同步请求源码解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class RealCall { override fun execute () : Response { check(executed.compareAndSet(false , true )) { "Already Executed" } timeout.enter() callStart() try { client.dispatcher.executed(this ) return getResponseWithInterceptorChain() } finally { client.dispatcher.finished(this ) } } } class Dispacher { private val runningSyncCalls = ArrayDeque<RealCall>() @Synchronized internal fun executed (call: RealCall ) { runningSyncCalls.add(call) } internal fun finished (call: RealCall ) { finished(runningSyncCalls, call) } }
从Dispatcher可以看出,同步请求单独放在runningSyncCalls队列中(和异步请求独立),因此同步请求的数量是没有限制的。
使用指南 OkHttpClient 在整个app中最好采用单例模式,所有的网路请求都使用一个OkHttpClient 实例。这是因为每个OkHttpClient 都会创建自己的连接池 和线程池 。如果每次请求都创建一个OkHttpClient就会浪费系统资源。
当所有请求都使用了同一个OkHttpClient后,如果某次网络请求需要特殊配置可以使用OkHttpClient::newBuilder()
,这种方式创建的client会共享连接池、线程池以及全局配置。
参考OkHttpClients Should Be Shared
编译okhttp 4.x
下载okhttp-4.10.x源码 、Java jdk17 、IntelliJ IDEA
使用IntelliJ IDEA 打开okhttp项目根目录。
Gradle JVM需要设置为jdk17,位置在IntelliJ IDEA 【File】->【Settings】->【Build,Execution,Deployment】->【Build Tools->Gradle】。
在IDEA中打开AsynchronousGet.kt
文件,然后点击运行main函数即可。
参考资料 OkHttp Android 网络优化-DNS优化 SNI Interceptors : 重要“深入交流“系列:Okhttp(二)拦截器的实现 OkHttp3 Cache HTTP 304状态码的详细讲解 Cache-Control no-cache与max-age=0的区别 浅谈http中的Cache-Control 谈谈OKHttp的几道面试题 okhttp如何实现dns拦截?Android端HTTPDNS+OkHttp接入指南