大家好,欢迎来到IT知识分享网。
1 OkHttp简介
目前主流的Android网络请求框架有OkHttp和Retrofit,不过,Retrofit底层使用的是OkHttp,其自身是不具备网络请求能力的。
OkHttp是由Square公司开发并共享开源的高效网络访问框架,使用简单,它替代了HttpUrlConnection和Apache的HttpClient。谷歌官方在Android 6.0 (API 23)里已移除HttpClient,加入了OkHttp。
默认情况下OkHttp具备以下特性:
- 支持
HTTP/2.0,HTTP/2.0是持久化连接,支持多路复用(客户端和服务端只有一个连接,通过这一个连接可以发起多重请求); - 连接池减少请求延时(如果
HTTP/2.0不可用); - 透明的
GZIP压缩下载大小; - 缓存响应内容,避免一些完全重复的网络请求;
- 网络出现问题后,
OkHttp能自动中恢复。如果服务器有多个IP地址,一个失败后,OkHttp会自动尝试连接其他的地址;
OkHttp/2.0是基于SPDY(SPeeDY)设计的。SPDY是谷歌开发的基于TCP的会话层协议,用于最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%。
speedy [ˈspiːdi] 迅速发生的;高速的,快速移动的
下面是Http常见的一些状态码:
100~199:指示信息,表示请求已接收,继续处理;200~299:请求成功,表示请求已被成功接收、理解;300~399:重定向,要完成请求必须进行更进一步的操作;400~499:客户端错误,请求有语法错误或请求无法实现;500~599:服务器端错误,服务器未能实现合法的请求;
2 OkHttp的使用流程
添加依赖:
dependencies { ... implementation("com.squareup.okhttp3:okhttp:4.9.3") }
在使用OkHttp进行请求时,首先要创建一个OkHttpClient的实例:
val client = OkHttpClient()
如果想要发起一条HTTP请求,就需要创建一个Request对象(HttpClient和Request都用了建造者模式):
val request = Request.Builder().url(url).build()
之后调用OkHttpClient的newCall方法来创建一个Call对象,并调用它的execute/enqueue方法来发送请求并获取服务器返回的数据,其中,execute()方法是同步方法,enqueue()方法是异步方法:
val response = client.newCall(request).execute()
response对象就是服务器返回的数据,可以使用如下写法来的到返回的具体内容:
val responseData = response.body?.string()
下面是OkHttp进行GET请求/POST请求的代码:
private val client = OkHttpClient() // 新建OkHttpClient客户端 // GET请求(同步) fun getMethod(url: String): String {
val request = Request.Builder().url(url).build() // 新建Request对象 val response = client.newCall(request).execute() // Response为OkHttp中的响应 return response.body?.string() ?: "" } // POST请求(同步) val JSON = "application/json; charset=utf-8".toMediaType() fun postMethod(url: String, json: String): String {
val body = RequestBody.create(JSON, json) val request = Request.Builder().url(url).post(body).build() val response = client.newCall(request).execute() return response.body?.string() ?: "" } // GET请求(异步) 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) {
e.printStackTrace() } override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response") for ((name, value) in response.headers) {
println("$name: $value") } println(response.body!!.string()) } } }) } // POST请求(异步) fun run() {
val formBody = FormBody.Builder() .add("search", "Jurassic Park") .build() val request = Request.Builder() .url("https://en.wikipedia.org/w/index.php") .post(formBody) .build() client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace() } override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response") for ((name, value) in response.headers) {
println("$name: $value") } println(response.body!!.string()) } } }) }
更多使用方式:https://square.github.io/okhttp/recipes/
基本的步骤就是创建 OkHttpClient、Request 和 Call/RealCall,之后调用 Call/RealCall.execute()/enqueue(...) 方法。RealCall 实现了接口 Call。
interface Call : Cloneable {
fun request(): Request @Throws(IOException::class) fun execute(): Response fun enqueue(responseCallback: Callback) fun cancel() fun isExecuted(): Boolean fun isCanceled(): Boolean fun timeout(): Timeout public override fun clone(): Call fun interface Factory {
fun newCall(request: Request): Call } } class RealCall( val client: OkHttpClient, / The application's original request unadulterated by redirects or auth headers. */ val originalRequest: Request, val forWebSocket: Boolean ) : Call {
... }
3 OkHttp的请求流程
以下是OkHttp发起请求的大致流程:
3.1 OkHttpClient
在使用OkHttpClient之前,需要先创建一个OkHttpClient客户端,OkHttpClient的构造方法如下:
open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {
constructor() : this(Builder()) }
除了直接创建 OkHttpClient 实例之外,还可以使用建造者模式。OkHttpClient.Builder里面的可配置参数如下:
open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {
constructor() : this(Builder()) 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 retryOnConnectionFailure = true // 重试连接失败 internal var cookieJar: CookieJar = CookieJar.NO_COOKIES internal var cache: Cache? = null internal var callTimeout = 0 internal var connectTimeout = 10_000 internal var readTimeout = 10_000 internal var writeTimeout = 10_000 internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher this.connectionPool = okHttpClient.connectionPool this.interceptors += okHttpClient.interceptors this.networkInterceptors += okHttpClient.networkInterceptors this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure this.cookieJar = okHttpClient.cookieJar this.cache = okHttpClient.cache this.callTimeout = okHttpClient.callTimeoutMillis this.connectTimeout = okHttpClient.connectTimeoutMillis this.readTimeout = okHttpClient.readTimeoutMillis this.writeTimeout = okHttpClient.writeTimeoutMillis } fun callTimeout(timeout: Long, unit: TimeUnit) = apply {
callTimeout = checkDuration("timeout", timeout, unit) } fun addInterceptor(interceptor: Interceptor) = apply {
interceptors += interceptor } fun build(): OkHttpClient = OkHttpClient(this) } }
获取 OkHttpClient 和 Request 实例之后,会调用 OkHttpClient.newCall(Reuqest) 方法,得到 Call/RealCall 对象。对于HttpClient.newCall方法,RealCall才是真正的执行者:
open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false) }
3.2 同步请求
同步请求是指发出网络请求之后当前线程被阻塞,直到请求的结果(成功或者失败)到来,才继续向下执行。同步请求使用的是execute方法,如下所示:
open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {
@get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher class Builder constructor() {
internal var dispatcher: Dispatcher = Dispatcher() } override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false) } class RealCall( // RealCall为真正的请求执行者 val client: OkHttpClient, val originalRequest: Request, val forWebSocket: Boolean ) : Call {
internal val eventListener: EventListener = client.eventListenerFactory.create(this) val call: RealCall get() = this@RealCall override fun execute(): Response {
check(executed.compareAndSet(false, true)) {
"Already Executed" } // 每个call只能执行一次 timeout.enter() callStart() try {
client.dispatcher.executed(this) // 通过dispatcher已经进入执行状态 return getResponseWithInterceptorChain() // 通过一系列的拦截器请求处理和响应处理得到最终的返回结果 } finally {
client.dispatcher.finished(this) // 通知dispatcher,已经执行完毕 } } } class Dispatcher constructor() {
/ Running synchronous calls. Includes canceled calls that haven't finished yet. */ private val runningSyncCalls = ArrayDeque<RealCall>() // 运行中的同步请求队列 @Synchronized internal fun executed(call: RealCall) {
runningSyncCalls.add(call) } }
async [əˈsɪŋk] 异步(asynchronous) sync [sɪŋk] 同步
同步请求是调度器dispatcher执行—client.dispatcher.executed(call: RealCall)来完成。
3.3 Dispatcher调度器
dispatcher [dɪˈspætʃər] [计] 调度程序;[计] 分配器
分发器主要是用来维护请求队列与线程池,完成请求调配。在创建HttpClient的时候,我们也可以传递自定义的线程池来创建分发器, 以下是Dispatcher的相关源码:
class Dispatcher constructor() {
// 并发执行的最大请求数,超过了这个数量之后,请求在内存中排队,等待正在运行的调用完成 @get:Synchronized var maxRequests = 64 set(maxRequests) {
require(maxRequests >= 1) {
"max < 1: $maxRequests" } synchronized(this) {
field = maxRequests } promoteAndExecute() } // 同一域名同时执行的最大请求数 @get:Synchronized var maxRequestsPerHost = 5 set(maxRequestsPerHost) {
require(maxRequestsPerHost >= 1) {
"max < 1: $maxRequestsPerHost" } synchronized(this) {
field = maxRequestsPerHost } promoteAndExecute() } // 闲置任务 @set:Synchronized @get:Synchronized var idleCallback: Runnable? = null // 异步请求等待执行队列 private val readyAsyncCalls = ArrayDeque<AsyncCall>() // 异步请求正在执行队列 private val runningAsyncCalls = ArrayDeque<AsyncCall>() // 同步请求正在执行队列 private val runningSyncCalls = ArrayDeque<RealCall>() // 异步请求使用的线程池 private var executorServiceOrNull: ExecutorService? = null // 创建线程池(懒加载) @get:Synchronized @get:JvmName("executorService") val executorService: ExecutorService get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS, SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false)) } return executorServiceOrNull!! } // 构造器,自定义线程池 constructor(executorService: ExecutorService) : this() {
this.executorServiceOrNull = executorService } }
Dispatcher 有两个构造方法,可以使用设定的线程池。如果没有设定线程池,则会使用默认的线程池,这个线程池比较适合执行大量的消耗比较少的任务。Dispatcher中的默认线程池配置保证了新加入的任务可以被立即执行,避免阻塞。
同步请求不需要线程池,也不需要做任何限制,所以分发器只是做一下记录,后续按照加入队列的顺序同步请求即可。
3.4 异步请求
异步请求是指网络请求发出之后,不必等待请求结果的到来,就可以去做其他的事情,当请求结果到来时,在做处理结果的动作。异步请求使用的是enqueue方法,如下所示:
open class OkHttpClient internal constructor( builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false) } class RealCall( // RealCall为真正的请求执行者 val client: OkHttpClient, val originalRequest: Request, val forWebSocket: Boolean ) : Call {
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) {
"Already Executed" } callStart() client.dispatcher.enqueue(AsyncCall(responseCallback)) } } class Dispatcher constructor() {
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 // 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) // 线程池执行任务 } return isRunning } internal inner class AsyncCall( private val responseCallback: Callback ) : Runnable {
} }
异步请求是调度器dispatcher执行—client.dispatcher.enqueue(call: AsyncCall),并通过回调(Callback)获取服务器返回的结果。
Dispatcher将call加入到队列中,然后通过线程池来执行call。Dispatcher是一个任务调度器,它内部维护了三个双端队列(新来请求放在队尾,执行请求从对头部取):
readyAsyncCalls:准备运行的异步请求;runningAsyncCalls:正在运行的异步请求;runningSyncCalls:正在运行的同步请求;
当 runningAsyncCalls 的数量小于 64 并且正在运行的请求主机小于 5 时,把请求添加到 runningAsyncCalls 中并在线程池中执行,否则就加入到 readyAsyncCalls 中进行等待执行。
4 OkHttp的拦截器
OkHttp拦截器就是基于责任链模式来实现的, 在请求到达时,拦截器会做一些处理(比如添加参数),然后传递给下一个拦截器处理:
OkHttp提供了一系列的拦截器来处理相应的业务。也可以通过自定义拦截器,来实现自己的拦截业务。下面是一些常用的拦截器:
retryAndFollowUpInterceptor:失败和重定向拦截器。 当请求内部抛出异常时,判定是否需要重试,当响应结果是3xx重定向时,构建新的请求并发送请求;BridgeInterceptor:应用层和网络层的桥接拦截器。 主要工作是请求添加cookie,添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要解压;CacheInterceptor:缓存拦截器。如果命中缓存,则不发起网络请求;ConnectInterceptor:连接拦截器,内部会维持一个连接池,主要负责TCP连接,包括连接复用、创建连接(三次握手)、释放连接以及创建连接上的socket流;networkInterceptor:网络拦截器,通常用于监控网络层的数据传输;CallServerInterceptor:请求拦截器,在前置准备工作完成后,真正发起网络请求;
OkHttp空闲连接如何清除?
- 在将连接加入连接池的时候就会启动定时任务;
- 有空闲连接的话,如果最长的空闲时间大于
5分钟或空闲数大于5,就关闭移除这个最长空闲连接;如果空闲数不大于5且最长的空闲时间不大于5分钟,就在时间到5分钟的时候清理; - 没有空闲连接,就等
5分钟后再尝试清理; - 没有连接不清理;
5 总结
OkHttp是一个网络请求框架,它是支持HTTP/2.0的,因此HTTP/2.0的一些优势也就体现在了这个框架上。比如说,HTTP/2.0是基于SPDY协议的,这个协议是用来减少网络延迟,提高网络速度的,再比如持久化连接、多路复用,客户端和服务端只有一个连接,可以通过这一个连接向服务端发起多重请求。如果HTTP/2.0不可以用,还可以使用连接池来减少延迟,连接池是HTTP/1.1提出的概念,连接池可以理解成是一个容器,我们创建的一些连接,在请求结束后,并不会断开,而是直接放到连接池中,下次直接拿来用就可以了。除了这些还有,比如缓存,如果命中缓存,可以避免重复的请求,如果网络出错,还可以自动恢复等等。
以下是OkHttp的请求流程:
- 通过建造者模式来创建
OkHttpClient和Request对象(建造者模式就是用来创建灵活性和可扩展性强的复杂对象); - 通过调用
OkHttpClient的newCall方法来获取一个RealCall对象,通过RealCall来实现同步请求或者异步请求。在RealCall的execute方法和enqueue方法中,是使用调度器Dispatcher来完成的; - 在
Dispatcher中维护了3个双端队列——准备运行的异步请求队列(readyAsyncCalls)、正在运行的异步请求队列(runningAsyncCalls)和正在运行的同步队列(runningSyncCalls)。如果是同步请求,则直接添加到同步请求队列中,等待顺序调用即可。如果是异步请求,如果正在运行的异步队列中的任务数未超过64,且同一域名的请求没有超过5个,则加入到正在运行的异步请求队列中,同时添加到线程池,否则加入到准备运行的请求队列中; OkHttp的拦截器使用的是责任链模式(为同一请求的接收者创建一个链,请求沿着这条链传递,如果某个接收者需要处理这个请求,就交给它处理,处理完成后再继续向下传递),方法名为getResponseWithInterceptorChain()。在OkHttp中有六个拦截器,第一个是失败和重定向拦截器,如果内部请求出现异常,判断是否需要重试;第二个拦截器是应用层和网络层桥接拦截器,主要负责Cookie的处理和添加固定的请求头;第三个拦截器是缓存拦截器,如果命中缓存,则不再进行网络请求;第四个拦截器是连接拦截器,其内部维护着一个的连接池,负责TCP的连接,包括连接复用、创建连接、释放连接以及创建连接上的Socket流;第五个拦截器是网络拦截器,主要用来监听网络上的数据传输;第六个拦截器是请求拦截器,当前置工作全部完成后,发起网络请求。
参考
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/121075.html




