okhttp——网络请求模型
简介
okhttp是Android中应用最广的http网络请求框架。结构优雅,性能强大。我们通过阅读它,对网络库的架构进行学习。本篇主要阅读okhttp的网络请求拦截链模型。
基本结构
okhttp采用拉截链的模型,将网络请求的各个部分,以一个个拦截器的方法,加入拦截链。
拦截链
详细代码
我们知道,在okhttp的任务调度模型中,最终任务,会调用execute
方法。我们先来看execute
方法。
override fun execute() {
var signalledCallback = false
transmitter.timeoutEnter()
try {
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)
}
}
这个方法中,实现网络请求的关键调用是:getResponseWithInterceptorChain
。其他主要还是调用回调或处理异常。
拼装拦截链
@Throws(IOException::class)
fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = ArrayList<Interceptor>()
interceptors.addAll(client.interceptors())
interceptors.add(RetryAndFollowUpInterceptor(client))
interceptors.add(BridgeInterceptor(client.cookieJar()))
interceptors.add(CacheInterceptor(client.internalCache()))
interceptors.add(ConnectInterceptor(client))
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors())
}
interceptors.add(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)
if (transmitter.isCanceled) {
closeQuietly(response)
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw transmitter.noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null)
}
}
}
这块儿代码基本还是简单清晰的。先用ArrayList<Interceptor>
保存拦截器的队列,然后生成RealInterceptorChain
,最后调用proceed
方法,获取response。
我们先来看拦截器的抽象实现。
/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
*/
interface Interceptor {
@Throws(IOException::class)
fun intercept(chain: Chain): Response
companion object {
// This lambda conversion is for Kotlin callers expecting a Java SAM (single-abstract-method).
@JvmName("-deprecated_Interceptor")
inline operator fun invoke(
crossinline block: (chain: Chain) -> Response
): Interceptor = object : Interceptor {
override fun intercept(chain: Chain) = block(chain)
}
}
interface Chain {
fun request(): Request
@Throws(IOException::class)
fun proceed(request: Request): Response
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
fun connection(): Connection?
fun call(): Call
fun connectTimeoutMillis(): Int
fun withConnectTimeout(timeout: Int, unit: TimeUnit): Chain
fun readTimeoutMillis(): Int
fun withReadTimeout(timeout: Int, unit: TimeUnit): Chain
fun writeTimeoutMillis(): Int
fun withWriteTimeout(timeout: Int, unit: TimeUnit): Chain
}
}
intercept
方法,就是拦截器的核心,输入Chain,返回Response。
我们随意找一个Interceptor来进行阅读
RetryAndFollowUpInterceptor
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Transmitter transmitter = realChain.transmitter();
int followUpCount = 0;
Response priorResponse = null;
while (true) {
transmitter.prepareToConnect(request);
if (transmitter.isCanceled()) {
throw new IOException("Canceled");
}
Response response;
boolean success = false;
try {
response = realChain.proceed(request, transmitter, null);
success = true;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), transmitter, false, request)) {
throw e.getFirstConnectException();
}
continue;
}
...
}
这一段逻辑中,我们传入的是chain,即整个拦截链,会在中间调用realChain.proceed()。表示,在当前拦截器中,我们只做我们职责之类的逻辑,其余的逻辑,交给传入Chain的下一环。
由此我们得知,RealInterceptorChain其实是一次请求所要做的所有工作。每一个Interceptor只负责一部分工作。它们的顺序是从外到内,当完成自己部分的外层功能后,就会将接下来的工作,交给下一层去完成。RetryAndFollowUpInterceptor只负责重试和重定向这些外层工作,其实逻辑会交由拦截器链的下一环节实现。Interceptor本身不用关心下一级的Interceptor是谁。
接下来,我们再看一下,RealInterceptorChain的逻辑。
RealInterceptorChain
RealInterceptorChain中有一个index的索引。它标识了当前拦截器链路进行到了哪一环。
我们着重看RealInterceptorChain的proceed
方法,看一下Interceptor是如何前进到下一环的。
class RealInterceptorChain(
private val interceptors: List<Interceptor>,
private val transmitter: Transmitter,
private val exchange: Exchange?,
private val index: Int,
private val request: Request,
private val call: Call,
private val connectTimeout: Int,
private val readTimeout: Int,
private val writeTimeout: Int
) : Interceptor.Chain {
private var calls: Int = 0
......
override fun proceed(request: Request): Response {
return proceed(request, transmitter, exchange)
}
@Throws(IOException::class)
fun proceed(request: Request, transmitter: Transmitter, exchange: Exchange?): Response {
if (index >= interceptors.size) throw AssertionError()
calls++
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw IllegalStateException("network interceptor " + interceptors[index - 1] +
" must retain the same host and port")
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw IllegalStateException("network interceptor " + interceptors[index - 1] +
" must call proceed() exactly once")
}
// Call the next interceptor in the chain.
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size && next.calls != 1) {
throw IllegalStateException("network interceptor " + interceptor +
" must call proceed() exactly once")
}
if (response.body() == null) {
throw IllegalStateException(
"interceptor $interceptor returned a response with no body")
}
return response
}
}
这一段代码首先对index进行了检查,然后对call,exchange中的种种参数进行了检查。最后调用了
// Call the next interceptor in the chain.
val next = RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
调用当前interceptor的intercept方法,并将下一个interceptor传入。
小结
okhttp的网络请求,采用了interceptor这样的结构,因为网络请求是一个层级深,分支少的结构。每一个层级并不关心下一个层级的实现。因此,这样的结构很合适。
发散一下,对于层级深,分支少,交付结果一致的业务模型,我们也可以采用这种interceptor的模型。方便层级之前解耦合。
如有问题,欢迎指正。