在面试中,OkHttp
做为咱们基本属于必用的第三方库来讲,也是一个很是重要的考点,因此对其原理的掌握也会让咱们的能力获得必定的提高。java
OkHttp官网地址:square.github.io/okhttp/git
先一段引入关于OkHttp的使用,这是直接拉取了官网挂着的使用方法。由于在通常的使用过程当中,后台可能会经过比较带有的session
或者cookie
来判断当前用户是否和缓存的用户相同,因此通常一个项目总体使用单例模式来建立OkHttpClient
的对象。github
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
复制代码
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder()
.url(url)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
复制代码
这是咱们的在okhttp
中使用的方法,整个项目的解析将围绕下面5个类进行。web
首先是OkHttpClient
和Request
。 为何这两个一块儿讲解呢?由于两个构造方式相同OkHttpClient
是一个全局掌控者,Request
是一个请求体的封装。面试
public final class Request {
final HttpUrl url; // 路径
final String method; // 请求方式
final Headers headers; // 请求头
final @Nullable RequestBody body; // 请求体
final Object tag;
}
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final Dispatcher dispatcher; // 分发器
final @Nullable Proxy proxy; //代理
final List<Protocol> protocols; //协议
final List<ConnectionSpec> connectionSpecs; //传输层版本和链接协议
final List<Interceptor> interceptors; // 拦截器
final List<Interceptor> networkInterceptors; // 网络拦截器
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector; //代理选择
final CookieJar cookieJar; //cookie
final @Nullable Cache cache; //缓存
final @Nullable InternalCache internalCache; //内部缓存
final SocketFactory socketFactory; //socket 工厂
final @Nullable SSLSocketFactory sslSocketFactory; //安全套接层socket 工厂,用于HTTPS
final @Nullable CertificateChainCleaner certificateChainCleaner; // 验证确认响应证书 适用 HTTPS 请求链接的主机名。
final HostnameVerifier hostnameVerifier; // 主机名字确认
final CertificatePinner certificatePinner; // 证书链
final Authenticator proxyAuthenticator; // 代理身份验证
final Authenticator authenticator; // 本地身份验证
final ConnectionPool connectionPool; // 链接池,复用链接
final Dns dns; // 域名
final boolean followSslRedirects; // 安全套接层重定向
final boolean followRedirects; // 本地重定向
final boolean retryOnConnectionFailure; // 重试链接失败
final int connectTimeout; // 链接超时
final int readTimeout; // read 超时
final int writeTimeout; // write 超时
final int pingInterval;
}
复制代码
能看到OkHttpClient
的内部元素不少,可是咱们不少时间并不会进行直接的使用,是由于他本身已经作了不少层的封装,另外他们这种建立对象的模式又称为建造者设计模式。设计模式
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
// 。。。。。
}
复制代码
对建造者设计模式作一个比较通俗的介绍,就是将咱们草稿图上的数据应用到真实的场景中去。promise
val client = OkHttpClient.Builder().build()
// 调用Builder()的builder()函数
// 最后是建立了OkHttpClient对象,咱们本来的数据是存储在OkHttpClient的Builder中
fun build(): OkHttpClient = OkHttpClient(this)
复制代码
可是说了这么久,仍是有一个问题啊,我没看到他对数据进行了使用啊??别着急,如今咱们进入咱们的使用环节了。缓存
接下来就是Call
这个类,根据模版写法,咱们知道须要将封装好的Request
请求体数据塞入OkHttpClient
中返回的就是一个Call
。安全
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
复制代码
经过进入newCall()
方法,咱们知道返回的数据实际上是实现Call
的接口一个具体类RealCall
,具体操做咱们不用知道,咱们只用知道返回的一个具体类是什么就能够了,由于日后的操做都是围绕一个具体的东西展开的。 在看模版的下一句话call.enqueue(...)
,进入函数,咱们能够看到下述的函数。服务器
override fun enqueue(responseCallback: Callback) {
synchronized(this) {
check(!executed) { "Already Executed" }
// 一个Call只能进行一次的执行操做
executed = true
}
callStart()
client.dispatcher.enqueue(AsyncCall(responseCallback)) // 1 -->
}
复制代码
其余都还好,直接看到上述最后一行代码,由于咱们须要将任务发布出去,而且拿到数据,那么天然须要一个分发器了和一个接收回馈数据的通道了,这显然就是咱们上文中OkHttpClient
中所看到的dispatcher
和咱们在外部定义好的Callback
==> responseCallback
。
internal fun enqueue(call: AsyncCall) {
// 使用同步机制对请求数据进行控制
synchronized(this) {
readyAsyncCalls.add(call)
// 我的理解:对同一个host发起多个请求是为了加快查询速度,减小资源浪费
// 他会从正在执行运行的Call中先进行查找,再从准备执行的Call中查找
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute() // 1 ==>
}
// 1 ==>
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()
// 正在运行的请求数量不能大于64个
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
// 能够存在的host数量为5个
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
}
复制代码
想来对整个的处理过程已经有一个比较详细的讲解了,可是咱们仍是没有看到数据的返回操做,甚至说具体的运行,不过咱们可以注意到一个中途意外冒出的变量executorService
,这个变量是从哪里来的呢?
溯源咱们可以发现,他在Dispatcher
中就已经有过了初始化操做。
@get:JvmName("executorService") val executorService: ExecutorService
get() {
if (executorServiceOrNull == null) {
executorServiceOrNull = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
}
return executorServiceOrNull!!
}
复制代码
一看到要说ThreadPoolExecutor
,哦哦哦哦!线程池,可是和什么线程池长得特别像呢?进入已经定义好的Executors
类中查找,可以查找到以下的代码段:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
复制代码
是否是有点像呢?那好,咱们就认定了它是咱们的CachedThreadPool
线程池。
ok!fine!用的线程池来进行异步操做,那确定就是说明里面有一个线程了,那这个线程是啥,咱们是否内心有点数呢?若是没有,也没啥关系,下面咱们将继续引出。
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this) // (1)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException) // (2)
} finally {
if (!success) {
client.dispatcher.finished(this) // (3)
}
}
}
复制代码
那接下来就又不是什么大问题了,主要就看到咱们的注释一、二、3。
executorService.execute(this)
:对于线程池而言运行的显然是线程,而this
几就是咱们的AsyncCall
,经过对AsyncCall
的观察咱们也是可以得知它是继承了Runnable
的,因此异步进行的操做来源咱们也已经清楚了。responseCallback.onFailure()
,也就是经过咱们传入的Callback
接收数据的错误反馈。client.dispatcher.finished(this)
:为何须要这个呢?其实他本来有这样的一段英文注释,This call is no longer running!
,也就是说明这个函数是为了通知Dispatcher
咱们的AsyncCall
已经完成了运行。又开始有问题了吧,看着就着急。咋就没看到responseCallback()
的onResponse
方法的使用呢???
那咱们作一个猜想吧,其实我看了一下基本也是正解了。咱们的不是Runnable
嘛,而数据是放在线程池中run()
来运行的,那么onResponse()
方法的出现应该是在run()
的这个函数中了。接下来咱们继续收看代码
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
val response = getResponseWithInterceptorChain() // (1)
signalledCallback = true
responseCallback.onResponse(this@RealCall, response) //(2)
} catch (e: IOException) {
// 。。。。。
responseCallback.onFailure(this@RealCall, e)
} catch (t: Throwable) {
// 。。。。。
responseCallback.onFailure(this@RealCall, e)
} finally {
client.dispatcher.finished(this)
}
}
}
复制代码
在这里的注释(2)中,咱们很幸运的看到了onResponse()
的方法调用了。好那接下来就是下一个问题了,Response
是从哪里来的????
上面不是写着嘛??getResponseWithInterceptorChain()
这个函数里来的呗。哇哦!!没错了,那它是怎么来的? 🤔🤔🤔
又要看代码了,好烦好烦。。。
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
// 咱们以前上面也出现过forWebSocket这个flag
// 其实它是okhttp为了长链接而准备的
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
)
val response = chain.proceed(originalRequest)
return response
}
复制代码
为了尽可能让代码简洁明了,我截取了一些关键代码,以供参考。
其实他就是经过一堆的拦截器来获取数据的,可是显然这里不是终点站,由于咱们看到的return
中就仍是一个函数,说明答案还在这个函数中。经过观察咱们很容易得知,这个的操做的具体类是一个叫作RealInterceptorChain
的类。
override fun proceed(request: Request): Response {
// 不断调用下一个拦截器对相应的数据进行返回
val next = copy(index = index + 1, request = request)
val interceptor = interceptors[index]
val response = interceptor.intercept(next)
return response
}
复制代码
Response
,固然这个数据你须要注意,并不必定是成功的数据,通常来讲数据成功的获取都须要走到咱们的响应拦截器以后才能真正的成功。
这里咱们须要重点讲解一下CacheInterceptor
这个类,咱们截取他的intercept()
方法,由于里面涉及了咱们面试时可能会频繁使用的响应码
override fun intercept(chain: Interceptor.Chain): Response {
// 依据咱们传入的request获得cache中缓存的response
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)
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// 本地查询到的网络请求和缓存数据皆为空的状况下
// 爆HTTP_GATEWAY_TIMEOUT,网关超时的错误
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()
}
// 没有网络状况下,直接使用咱们本地的数据缓存
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build()
}
// 调动责任链中下一轮的拦截器,来获取数据
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 (cacheResponse != null) {
// HTTP_NOT_MODIFIED:304,说明咱们本地的缓存是最新的
// 没有必要将数据从服务器拉取进行更新了
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
} 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)
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
复制代码
最后咱们经过一张图来完成对整个OkHttp
的工做流程梳理。