一、概述
为什么要是用OKHttp3
总结下来就两个大的方面
一、成熟稳定
OkHttp距今已有10多年的历史,在Android中大量且广泛的应用,在大、中、小项目中无处不在。可以这样说,只要是一个Android项目,网络框架的底层必定是OKHttp
二、高效
1.OkHttp的底层使用socket做数据的收发,对于同一台主机的多个请求可以公用一个socket连接,而不是没发送一次Http请求就关闭连接。连接复用后就可以极大的提高Http请求及响应的速度。因为请求的时候需要经历三次捂手,关闭的时候需要经过四次挥 手。一旦复用,这两步骤都省略,效率核新能就提上去了。
2.连接池,新开链接是比较消耗时间和资源的,如果新开了一个链接使用后放入连接池,下次有相同类型的连接到来时直接从连接池拿而不是重新创建一个,这样可以极大的提升请求及响应的时间。
3.OKHttp支持GZIP透明压缩,这样可以减少响应数据的大小,网络请求响应的速度
4.OkHttp还支持Http/2,用于进一步提高响应速度,由于HTTP2支持头部压缩,相同类型的请求没请求一次相同的头部信息就会可以少发送一部分。相比一Http1.1其可以进行多路复用,对请求进行交错发送,进一步提升请求及响应的速度
5.天然支持TLS,可以很方便的设置证书
6.可以自定义拦截器,用于拦截请求及响应内容
7.底层使用了okio,快速、稳定、内存消耗小,可以提升io的性能,从而提升okhttp的整体性能
okio采用了segment机制进行了内存共享,极大减少了copy操作带来的内存消耗,加快了速写速度。
okio引入了ByteString,使Byte[]与String之间的转换速度非常快
okio的segment进行内存复用,上传大文件时完全不用担心OOM
8.可以对DNS进行自定义解析
二、快速入门
第一步:初始化OKHttp
okHttp = OkHttpClient.Builder() .connectTimeout(20, TimeUnit.SECONDS)//链接超时为2秒,单位为秒 .writeTimeout(30, TimeUnit.SECONDS)//写入超时 .readTimeout(20, TimeUnit.SECONDS)//读取超时 .build()
第二步:创建一个请求并塞入请求体
var json = "application/json; charset=utf-8".toMediaTypeOrNull(); val requestBody = RequestBody.create(json,Gson().toJson(PostBean(Account("13386050182","123456")))) val request = Request.Builder() .url(BASE_URL + "tony/accounts/login") .addHeader("Content-Type", "application/json") .post(requestBody) .build()
第三步:创建一个call
val call = okHttp.newCall(request)
第四步:使用这个call发起请求
call.enqueue(object : Callback { //异步执行方法获取回调 override fun onFailure(call: Call, e: IOException) { e.printStackTrace() KLog.e(e.message) } override fun onResponse(call: Call, response: Response) { KLog.e("获取响应结果:${response.body?.string()}") } })
三、主流程原理分析
主流程调用流程图:
1.OKHttp会创建一个用于发起请求的Call,并且可以发送同步或异步的请求(我们以异步请求举例)
override fun newCall(request: Request): Call { return RealCall.newRealCall(this, request, forWebSocket = false) }
2.call调用enqueu发起异步请求,call只是一个接口,真正调用enqueue的是RealCall,即call的实现类
override fun enqueue(responseCallback: Callback) { synchronized(this) { check(!executed) { "Already Executed" } executed = true } transmitter.callStart() client.dispatcher.enqueue(AsyncCall(responseCallback)) } 可以从上面看到queue方法其实就是向dispatcher的enqueue方法中丢了一个Call对象。dispatcher的enqueue内部其实就是把Call扔进dispatcher的队列中。如下: internal fun enqueue(call: AsyncCall) { synchronized(this) { readyAsyncCalls.add(call)//加入准备队列 // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to // the same host. if (!call.get().forWebSocket) { val existingCall = findExistingCallWithHost(call.host()) if (existingCall != null) call.reuseCallsPerHostFrom(existingCall) } } promoteAndExecute()//把call从准备队列放入运行队列 } 下面看下promoteAndExecute()方法的内容 private fun promoteAndExecute(): Boolean { 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 // Max capacity. if (asyncCall.callsPerHost().get() >= this.maxRequestsPerHost) continue // Host max capacity. 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)//如果执行队列中有Call那就执行,RealCall的executeOn方法并把线程池传递进去 } return isRunning } 而executeOn方法也只是把当前要执行的代码加入线程池中而已,真正执行的其实是RealCall的run方法。如下: fun executeOn(executorService: ExecutorService) { ....省略 try { executorService.execute(this)//把当前线程扔入线程池 success = true } catch (e: RejectedExecutionException) { ....省略 responseCallback.onFailure(this@RealCall, ioException) } finally { if (!success) { client.dispatcher.finished(this) // This call is no longer running! } } } 执行的这个run方法就非常重要了,几乎所有的重要方法都在这里面。如下: override fun run() { threadName("OkHttp ${redactedUrl()}") { .....省略不重要代码 try { //这句代码非常关键,请求在拦截器当中一级一级的传递,最终发起请求,然后获取请求结果再层层通过拦截器传递给客户端。返回一个response val response = getResponseWithInterceptorChain() signalledCallback = true //执行请求成功的回调函数 responseCallback.onResponse(this@RealCall, response) } catch (e: IOException) { if (signalledCallback) { // Do not signal the callback twice! Platform.get().log(INFO, "Callback failure for ${toLoggableString()}", e) } else { responseCallback.onFailure(this@RealCall, e)//执行球球失败的回调函数 } } finally { client.dispatcher.finished(this) } } } }
3.getResponseWithInterceptorChan()里面主要是一层层的拦截器,如下:
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(interceptors, transmitter, null, 0, originalRequest, this, client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis) var calledNoMoreExchanges = false try { val response = chain.proceed(originalRequest)//执行链式调用过程,最终返回response if (transmitter.isCanceled) { response.closeQuietly() throw IOException("Canceled") } return response } catch (e: IOException) { calledNoMoreExchanges = true throw transmitter.noMoreExchanges(e) as Throwable } finally { if (!calledNoMoreExchanges) { transmitter.noMoreExchanges(null) } } }
4.分析到这里其实主流程就结束了。再来回顾一下
1.创建一个Call
2.发起异步调用call.enqueue
3.通过dispatcher.enqueue把call加入队列
4.线程池执行RealCall的run方法
5.执行拦截器链式调用发起请求及获取响应
6.响应成功/失败调用回调函数
7.完成