1,概述
客户端使用的网络框架,
简单来说几个关键字:同步异步/拦截器
2,实例
public class Get {
private static final String TAG = "Get";
public static final String URL = "http://www.baidu.com";
public static void test(){
// 创建客户端
OkHttpClient client = new OkHttpClient();
//创建请求
Request.Builder builder = new Request.Builder();
Request request = builder.url(URL)
.get() // 默认get
.build();
Request postRequest = new Request.Builder().post(
new FormBody.Builder().add("key","value").build()
).url(URL).build();
Call call = client.newCall(request);
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
Log.d(TAG, "onResponse");
}
});
try {
// 执行,一个call只可以执行一次,通过cas判断
Response execute = call.execute();
//同步请求
if (execute.isSuccessful() && execute.body() != null){
Log.d(TAG, " "+ execute.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3,源码解读
使用起来很简单,咱们分析下源码,看看如何发起一个网络请求;
从异步请求说起,传入回调;
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.d(TAG, "onFailure");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
Log.d(TAG, "onResponse");
}
});
执行RealCall.enqueue,进入异步队列,继续跟进,
这会,会用acs判断是否启动过一次,如果重复exqueue抛异常处理;
override fun enqueue(responseCallback: Callback) {
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
跟进client.dispatcher分发器,这儿将RealCall置换为AsyncCall,client是OkHttpClient,因为Call是从OkHttpClient.newClient创建,自然将自己传入RealCall构造方法中,
进入分发器enqueue方法,复用同host的call,看起来是一种优化;
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
假设没有复用,那么只是单纯加入readAsyncCalls队列,接着调用promoteAndExecute方法;
进入此方法会assert是否持有锁,否则会抛异常,防止死锁发生;
然后,简单创建一个list,用来保存可执行的call;
加锁,迭代readyAsyncCalls队列,检查call;
这有一个最大执行限制maxRequests,查看源码为64,对同一host也有最大限制,maxRequestsPerHost为5;
等一切检查通过,asynCall自增调用次数,加入executableCalls和runningAsyncCalls队列;
接下来,在可执行list中,调用AsyncCall.executOn方法,传入线程池ExecutorService;
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
}
如果不满足线程池执行条件,回调call.onFailure,即我们传入的回调;
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.run方法查看,
此处涉及tineout机制、拦截器机制;
当然,如果此处成功返回一个response,自然回调response.onResponse方法,执行我们传入的异步逻辑,否则在异常处会调用onFailure方法;
最后,在finally处finish自己,即还原一些配置,如主机数目(acs),从runningAsyncCall移除自己等;当然,如果移除失败,抛出异常,再做一些处理,如让分发器再次调用promoteAndExecute方法,初始队列call执行;
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)
}
}
}
现在重点方法是getResponseWithInterceptorChain,即通过拦截器访问真正的网络,看了android底层源码后,这个函数也不长(手动狗头);
函数逻辑清晰,首先这在RealCall中,先创建拦截器list;
将一些list加入此拦截器list,如OkHttpClient的拦截器等等等等,再写入RealInterceptorChain拦截链中,同时传入一些参数,如call本身,保存在call中的request等,
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
interceptors += client.interceptors
interceptors += RetryAndFollowUpInterceptor(client)
interceptors += BridgeInterceptor(client.cookieJar)
interceptors += CacheInterceptor(client.cache)
interceptors += ConnectInterceptor
if (!forWebSocket) {
interceptors += client.networkInterceptors
}
interceptors += CallServerInterceptor(forWebSocket)
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 {
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)
}
}
}
执行拦截链processd方法,不过看到这有个疑问,创建拦截链的时候不是将request传入了吗?看来这么写说明拦截链是可复用多个request的;
此方法前,检查参数,如拦截器index,为什么这么检查呢?check函数接受false,就抛出异常;
calls计数;
exchange是个Exchange类,注释说:“Excjamge是传输单个 HTTP 请求和响应对。 这在ExchangeCodec上分层连接管理和事件,它处理实际的 I/O。”
看来处理io就在exchange中了,重点是exchange.finder类的sameHostAndPort方法,接收url,此处check检查拦截器是否作用相同host,否则抛出异常;
再次检查calls,第一次执行process为true,看来这种check编程方式挺好,kotlin语言特性;
于是乎,进入真正拦截操作,index++,request不变,按顺序执行拦截器,调用intercept方法,
@Throws(IOException::class)
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 the next interceptor in the chain.
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,OkHttp提供拦截器有如下几个,
随便看一个,如ConnectInterceptor,这个用于打开与service的连接,显然是上述拦截器加入的最后一个,因为拦截器之前是可以做一些缓存策略的,如CacheInterceptor;
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
再看下CacheInterceptor,别说,这个intercept函数还挺长;
主要就是缓存思想嘛,如果对于相同的request,直接从缓存中拿去,如果缓存中不存在,则真实访问后放入缓存,并且设置有效时间;当然还有更多细节,需要研究这段代码,但缓存思想本质是一样的;
class CacheInterceptor(internal val cache: Cache?) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
networkResponse = chain.proceed(networkRequest)
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
// If we have a cache response too, then we're doing a conditional get.
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
还有更多拦截器,如重试重定向、桥接呀等等,本文更新在后文研究,现在本文是书写OkHttp访问网络全流程,继续回到chain.proces方法处,代码后半段如下,
@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
process方法如何递进调用,当然是在拦截器中执行intercept方法递进调用,
在process中,有个next,本质是拦截链的copy版本,不过将index++而已罢,所以process开头check(inde<size)就是这个意义,这就是设计模式中责任链模式;
ok,当所有拦截链函数调用完毕,返回response,这就是咱们拿到的response了;