大體的請求流程以下 html
請求大體被分爲了三個階段:準備、隊列、預處理。markdown
OkHttpClient是一個請求端,咱們能夠經過new來建立一個OkHttpClient:cookie
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流程。app
咱們能夠將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對象:socket
Call x = httpClient.newCall(request);
//newCall方法內:返回了一個RealCall對象;
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
複製代碼
而RealCall的構造函數:async
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對象。
若是咱們採用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的實現,後面的代碼大致上差不太多。
如今回到得到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()。
不管是同步請求,仍是異步請求,咱們最終都走到了這個方法:
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的回調處。