文章目录
本文基于OkHttp版本,该版本是用Kotlin实现:
com.squareup.okhttp3:okhttp:4.7.2
1-基本流程
OkHttp这个框架就不用过多介绍了吧,一句话概括就是对Socket编程的封装实现,方便实现网络通信。什么是Socket编程?Socket是TCP/IP协议的抽象实现,详情可以去看看:一篇文章带你熟悉 TCP/IP 协议-(一)
String testUrl = "https://wanandroid.com/wxarticle/chapters/json";
//@1.初始化OkHttpClient
OkHttpClient client = new OkHttpClient();
//@2.根据url构建Request
final Request request = new Request.Builder().url(testUrl).build();
//@3.发起同步请求
Response syncResponse = client.newCall(request).execute();
//@4.发起异步请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//TODO 失败回调 }
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//TODO 成功回调}
});
@1.第一步当然是初始化工作了,通过Builder模式构建OkHttpClient。主要是初始化一些基本参数配置,包括任务调度器、连接池、拦截器、线程池等。后面会详细讲述这些内容。核心是任务调度器和拦截器,任务调度器Dispatcher主要是维护了三个任务队列:
- readyAsyncCalls = ArrayDeque()
异步方式待执行的任务队列 - runningAsyncCalls = ArrayDeque()
异步方式正在执行的任务队列 - runningSyncCalls = ArrayDeque()
同步方式执行的任务队列
拦截器机制后面将作为重点讲述。
@2.第二步开始构造请求内容。根据Http协议我们知道发起网络请求的报文协议是请求行+请求头+请求体。这一步就是通过Builder模式将http协议报文的必要信息封装到一个Request对象,后面会将该Request对象转为符合http协议的报文发起一个网络请求。
internal constructor(request: Request) {
this.url = request.url//将字符串url转换成HttpUrl对象,便于提取Url中的scheme、path等信息
this.method = request.method//请求方法post、get等
this.body = request.body//请求体,post方法需要将内容放在请求体
this.tags = if (request.tags.isEmpty()) {
mutableMapOf()
} else {
request.tags.toMutableMap()
}
//请求头,这里是用数组实现,
//key、value、key、value这样存储键值对,所以肯定是偶数个
this.headers = request.headers.newBuilder()
}
@3、@4本质上原理是一样的,只是一个是同步实现一个是异步实现,同步异步不做多解释,我们就看看同步请求的实现来了解其原理。client.newCall(request).execute()
这里就是通过传入request,创建了一个Call的实现类RealCall的对象,并调用其execute方法执行请求任务,执行完毕返回一个Resonse对象。
override fun execute(): Response {
synchronized(this) {
check(!executed) {
"Already Executed" }
executed = true
}
timeout.enter()
callStart()
try {
//将请求任务添加到dispatcher的runningSyncCalls队列
client.dispatcher.executed(this)
//@5.通过拦截器链一步步处理请求任务
return getResponseWithInterceptorChain()
} finally {
//任务执行完毕从runningSyncCalls队列移除该任务
client.dispatcher.finished(this)
}
}
@5.通过拦截器链一步步处理请求任务。这里就是OkHttp框架的精髓了,将网络请求Request通过一个InterceptorChain加工处理生产出请求结果Response。InterceptorChain就像一条流水生产线,链式调用、分工明确。直接看代码吧:
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
val interceptors = mutableListOf<Interceptor>()
//(1)用户自定义顶层全局拦截器,优先级最高
interceptors += client.interceptors
//(2)错误、重定向拦截器
interceptors += RetryAndFollowUpInterceptor(client)
//(3)桥接拦截器,桥接应用层与网络层,添加必要的头信息
interceptors += BridgeInterceptor(client.cookieJar)
//(4)缓存处理拦截器
interceptors += CacheInterceptor(client.cache)
//(5)连接拦截器。建立C与S的Socket连接
interceptors += ConnectInterceptor
//(6)用户自定义的NetworkInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
//(7)访问服务端拦截器。
interceptors += CallServerInterceptor(forWebSocket)
//通过这些拦截器构造拦截器链RealInterceptorChain
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
try {
//通过RealInterceptorChain按顺序执行拦截器,最终得到Response
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
先大概了解下RealInterceptorChain是如何一步步将Request对象加工成Response对象的,第二章再分析各Interceptor的原理。前面将这些拦截器按顺序添加到了interceptors中,所以RealInterceptorChain.proceed方法就是从index=0开始执行对应拦截器的intercept方法,流程如下(第3章流程总结中有总的流程图):
- (1)用户自定义的Interceptor(可选)。通过OkHttpClient.Builder().addInterceptor添加,实际应用中会在此流程中往request添加一些全局的业务参数,比如cuid、userId、位置信息等
- (2)RetryAndFollowUpInterceptor。开启无限循环,执行后续拦截器步骤,根据返回的response
- 不需要重试或重定向或者不能再重试、重定向,直接返回response跳出循环
- 重试或重定向,更新request并再次执行后续步骤
- (3)BridgeInterceptor。
- 请求前,将Request对象转换为包含http协议信息的请求
- 执行后续拦截器
- 请求后,将返回结果Gzip解压出响应体内容
- (4)CacheInterceptor。
- 请求前,判断缓存策略,强制缓存则直接返回缓存,不再发起网络请求
- 执行后续拦截器
- 请求后,对比缓存策略则使用缓存,否则使用网络返回response,更新缓存
- (5)ConnectInterceptor
- 判断当前连接是否可用,不可用则从连接池中获取连接
- 获取失败则新建连接并执行TCP三次握手建立连接
- 执行后续流程
- (6)NetworkInterceptor(可选)。在建立连接后–>正式发送请求前的过程,用户进行一些处理。
- (7)CallServerIntercepto
- 请求前,将请求request encode为http协议的报文
- 请求后,从HTTP响应报文decode出response
2-拦截器
2.1-RetryAndFollowUpInterceptor
经历了用户自定义的Interceptor,第一个拦截器就是RetryAndFollowUpInterceptor,该拦截器主要处理重试和重定向,所以其实现是递归调用RealInterceptorChain.proceed方法执行后续步骤,根据递归结果判断是否需要重试/重定向。来看代码:
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
。。。//代码省略
//开启循环
while (true) {
call.enterNetworkInterceptorExchange(request, newExchangeFinder)
var response: Response
var closeActiveExchange = true
try {
if (call.isCanceled()) {
//请求取消
throw IOException("Canceled")
}
try {
//递归调用,执行后续Interceptor步骤
response = realChain.proceed(request)
newExchangeFinder = true
}