1.简介
大致的请求流程如下
请求大致被分为了三个阶段:准备、队列、预处理。
- 其中准备包括OkHttpClient、Request等等的实例化;
- 而队列阶段在同步执行的网络请求由于直接调用:execute(),所以不存在队列阶段,并且会因为网络I/O阻塞掉当前的线程。而enqueue()方法则是异步方法,会通过Dispatcher利用ExecutorService实现,而最终进行网络请求时和同步execute()接口一致;
- 预处理:预处理则是针对网络请求的预处理,OkHttp的实现方式是采用了一个拦截器链(Interceptor Chain),这一系列的拦截器将会为我们的网络请求添加一些header信息,或者是预定好请求失败的重试方式、从连接池中获取可用的实例以复用等等。接下来将会按照这三个阶段进行阅读源代码。
2.准备阶段
OkHttpClient是一个请求端,我们可以通过new来创建一个OkHttpClient:
OkHttpClient httpClient = new OkHttpClient();
点进入方法内,找到空参数的构造方法:
//OkHttpClient.decompiled.Java
public OkHttpClient() {
this(new OkHttpClient.Builder());
}
发现,我们实际上默认传入了一个Builder对象,而这个Builder则树我们的一个配置对象,我们可以通过Builder来对OkHttpClient进行一个配置,Builder的构造函数如下:
public Builder() {
this.dispatcher = new Dispatcher();
this.connectionPool = new ConnectionPool();
boolean var1 = false;
this.interceptors = (List)(new ArrayList());
var1 = false;
this.networkInterceptors = (List)(new ArrayList());
this.eventListenerFactory = Util.asFactory(EventListener.NONE);
this.retryOnConnectionFailure = true;
this.authenticator = Authenticator.NONE;
this.followRedirects = true;
this.followSslRedirects = true;
this.cookieJar = CookieJar.NO_COOKIES;
this.dns = Dns.SYSTEM;
this.proxyAuthenticator = Authenticator.NONE;
SocketFactory var10001 = SocketFactory.getDefault();
Intrinsics.checkNotNullExpressionValue(var10001, "SocketFactory.getDefault()");
this.socketFactory = var10001;
this.connectionSpecs = OkHttpClient.Companion.getDEFAULT_CONNECTION_SPECS$okhttp();
this.protocols = OkHttpClient.Companion.getDEFAULT_PROTOCOLS$okhttp();
this.hostnameVerifier = (HostnameVerifier)OkHostnameVerifier.INSTANCE;
this.certificatePinner = CertificatePinner.DEFAULT;
this.connectTimeout = 10000;
this.readTimeout = 10000;
this.writeTimeout = 10000;
this.minWebSocketMessageToCompress = 1024L;
}
这提供了一长串的构造参数,具体的意义稍候再说,我们只需要知道OkHttpClient的Build流程。
我们可以将OkHttpClient理解为一个请求器,它用来将请求发送出去,而请求就是Request,我们在声明完OkHttpClient后,需要声明Request对象:
Request request = new Request.Builder()
.url(url).post(requestBody).build();
其中的URL自然而然就是请求的地址,而RequestBody则是请求的请求体,本身是一个抽象类,我们可以设置请求的MediaType、Content和ContentLength等等。如下代码就设置了Content-Type为“text/html”(create方法中还会在其后拼接上:"; charset=utf-8"),请求的数据为data,这个data为表单数据,最终会存放在请求的body中,格式为var1=x&var2=y。
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html;charset=utf-8"), data);
接下来,我们通过OkHttpClient.newCall调用生成一个Call对象:
Call x = httpClient.newCall(request);
//newCall方法内:返回了一个RealCall对象;
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
而RealCall的构造函数:
class RealCall(
val client: OkHttpClient,
/** The application's original request unadulterated by redirects or auth headers. */
val originalRequest: Request,
val forWebSocket: Boolean
) : Call {
//Omit
}
我们在newCall方法中,填入了OkHttpClient、基本的请求、以及是否是WebSocket请求三个参数。这步走完,最终得到了一个Call对象。
3.同步请求
如果我们采用RealCall.execute()方法,那么将会走同步请求,即在当前线程执行这个请求,而execute方法如下:
//kotlin版本
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)
}
}
//Java版本
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
这里由于OkHttp后来使用Kotlin重写了,所以暂时按照Java的来说明:Java中的executed是一个boolean类型的变量,用于检查该RealCall是否已经被execute过了,配合sychronized加锁,这也说明了这个ReadCall只会被执行一次。
dispatcher是client的成员之一,可以视作事件的分发器,调用executed(this)即执行网络请求,而getResponseWithInterceptorChain();则是走了第三步:预处理;使整个请求通过Interceptor Chain,最终获得Response,如果获得的Response为空,则说明被拦截器链所取消了。
而Kotlin版本中,executed被声明成了:“可以用原子方式更新的boolean值”,即高并发下,保证只有一个线程能够访问,其实就等同于Sychronized(){}包裹的一段代码。 compareAndSet(原先的值,要修改的值),可以参考CAS的实现,后面的代码大体上差不太多。
4. 队列(异步请求)
现在回到获得RealCall对象的时候,我们通常不希望网络请求在主线程直接执行,这样会导致画面无法及时渲染出帧,影响用户体验,甚至会在Android中导致ANR。我们通过:
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
}
});
来将一个请求插入请求队列,其中我们要重写两个回调函数,分别是失败、成功时的回调函数。
enqueue的方法体如下:
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed"}
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
同样地,一个RealCall只能执行一次,CallStart()是开始执行的一个回调,而最后通过Dispatch进行事件分发,将一个异步的AsyncCall添加到发送队列中:
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
//如果不是WebSocket
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)//重用一个已经存在的Call
}
}
promoteAndExecute()//通知线程池去取出一个Call来处理;
在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
}
//遍历executableCalls
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
首先,遍历readyAsyncCalls,有两种特殊情况:
- 当runningAsyncCalls超过maxRequests时,说明线程要处理的任务已经满了,直接跳出循环;
- 当asyncCall.callsPerHost().get()也就是当前Call所对应的host已经接受了足够多的Call请求,那么循环继续; 如果二者都不满足,则执行以下:(主要是往executableCalls中添加执行的请求和往runningAsyncCalls里添加要执行的请求。)
然后,遍历刚才的得到的executableCalls,并用调用线程池执行任务。具体是调用asyncCall的executeOn(executorService())方法实现的。
executeOn方法的方法如下:
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
而AsyncCall的类如下:
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
private set
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
val host: String
get() = originalRequest.url.host
val request: Request
get() = originalRequest
val call: RealCall
get() = this@RealCall
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
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("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
}
终于,我们在重写的run()方法中,看到了和直接调用RealCall.execute相似的代码,通过getResponseWithInterceptorChain将一个请求通过第三步的拦截器链处理,再发送出去,最终成功获取到Response或者失败,报错。
try {
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {//omit }
这里的responseCallBack是AsyncCall在构造时,传入的构造函数,调用处就在enqueue方法中:
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
这个responseCallback就是我们在
httpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {}
});
中new 出的Callback()。
5. 预处理(拦截器链Interceptor Chain)
不论是同步请求,还是异步请求,我们最终都走到了这个方法:
getResponseWithInterceptorChain()
责任链模式的拦截器,整体可以在请求发出前再进行统一的包装,包括给请求加上请求头(Cookie或者是Token)、拦截不正确的请求等等。内置有五层拦截器,不包括(newWork拦截器),该方法的方法体如下:
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors//0(用户自定义的App拦截器,非netWorkInterceptor)
interceptors += RetryAndFollowUpInterceptor(client)//1
interceptors += BridgeInterceptor(client.cookieJar)//2
interceptors += CacheInterceptor(client.cache)//3
interceptors += ConnectInterceptor//4
//如果不是WebSocket,则添加用户自己自定义的netWork拦截器
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)//5
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 {
//focus
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)
}
}
}
最终,走到了:
val response = chain.proceed(originalRequest)
点开proceed方法:
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// Call 链中的下一个拦截器
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
Interceptor是对拦截器的抽象接口定义,在intercept方法中,将会接收一个Chain。而Chain就是拦截链的抽象定义,这样一来,当拦截器的具体实现在调用intercept方法时,就可以通过Chain拦截链,调用request方法取出客户端请求,然后再调用process方法从而创建下一个节点的Chain,这时,在下一个节点的Chain中,将会定位到下一个Interceptor拦截器。由此类推,直至最后一个拦截器Interceptor,不再执行Chain的process方法。因为执行到最后一个拦截器时,后面不再有拦截器,所以在最后一个拦截器调用后,就需要将最终的响应结果,反馈给客户端了。各个拦截器的职责如下:
在getResponseWithInterceptorChain()方法中,我们传入最原始的:originalRequest
val response = chain.proceed(originalRequest)
而在RetryAndFollowUp拦截器中,我们找到:
//RetryAndFollowUpInterceptor.kt
class RetryAndFollowUpInterceptor(private val client: OkHttpClient) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
// 省略
var request = chain.request
// 省略 ······
response = realChain.proceed(request)
// 省略
}
//······
}
我们在该拦截器中,有一次调用了chain.proceed(request),而BridgeInterceptor同理:
//BridgeInterceptor.kt
val networkResponse = chain.proceed(requestBuilder.build())
这里甚至传入了一个新的利用RequestBuilder.build()构造的request,最终走到最后一层拦截器:CallServerInterceptor中,没有了chain.proceed,取而代之的是:
var response = responseBuilder
.request(request)
.handshake(exchange.connection.handshake())
.sentRequestAtMillis(sentRequestMillis)
.receivedResponseAtMillis(System.currentTimeMillis())
.build()
不出意外的话,我们在此处将会拿到这个请求的结果,然后逐层返回,返回到CallBack的回调处。