OkHttp深入理解(1)综述

本文详细解析了OkHttp的同步及异步请求流程,包括如何通过责任链模式处理请求过程中的多个Interceptor,以及异步请求如何通过Dispatcher进行调度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      用OkHttp这么久,一直只知道基本用法, 没有去深入探究,以至于遇到一些需求的时候不知道该如何实现,在网络请求部分中写了许多冗余代码,对于有代码洁癖的我来说简直太痛苦了。现在查阅了许多资料,也慢慢看了一些源码,总算有了粗略的了解。
    OkHttp的总体流程大致如下:

      
    首先通过OkHttpClient.Builder创建一个client对象,然后通过Request.Builder构建一个Request对象,并用client.newCall(request)方法创建一个call出来。然后通过call.execute()进行同步请求,或者通过call.enqueue(Callback)进行异步请求。


同步请求

@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}

    上面这段代码是进行同步请求的代码,可以看到try代码块中第一行调用了getResponseWithInterceptorChain方法来获取Response。在这个方法里,会依次添加自定义的Interceptor、RetryAndFollowUpInterceptor、BridgeInterceptor、CacheInterceptor、ConnectInterceptor、自定义的networkInterceptor、CallServerInterceptor。并通过构建Chain来进行链式调用。代码如下:
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);
}

    最后一行中返回了chain.proceed(request),这个方法会返回一个Response对象。在这个方法内部会通过自增递归方式依次调用完链中的每一个Interceptor,代码较多,截取一部分如下:

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

    在chain.proceed(Request)方法内部,会先将Interceptor数组的下标index+1,创建出链中的下一个需要被调用的interceptor的链节点next。然后调用当前这个节点的interceptor的intercept(Chain)方法,将下一个链节点next传进去,下一个链节点next的proceed方法将会在当前这个节点的intercept(Chain)方法中被调用。通过这样的流程,完成对链中每一个Interceptor的调用,并在获取到Response之后,沿原路依次返回,并依次被各个Interceptor处理,最后返回到最初调用getResponseWithInterceptorChain()处。
    在这整个过程中采用了责任链模式进行处理,责任链模式将每个处理者(在这里即是Interceptor)连成一条链,每个节点持有下一个节点的引用(每次调用Interceptor.intercept(Chain)方法时,都会接收到传入的下一个节点Chain对象)。请求从链头开始沿着链依次传递,直到有一个处理者能处理为止(在这里是链上的最后一个节点CallServerInterceptor进行最终的请求发起并返回Response)。这里采用责任链模式的好处在于将请求与处理者之间解耦,发出请求的客户端并不需要知道到底是链上哪一个处理者对请求进行了处理,使得我们可以在不影响客户端的情况下动态地重新组织和分配责任(添加或移除Interceptor)。



异步请求
    通过调用call.enqueue(Callback)方法进行异步请求,在enqueue方法中最终通过调用call中持有的client对象的client.dispatcher().enqueue(new AsyncCall(Callback))方法进入请求队列。
    Dispatcher是作为一个异步请求分发策略的存在。里面维护了如下几个变量:
        ①、maxRequests = 64:最大并发请求数为64。
        ②、maxRequestsPerHost = 5:每个主机最大请求数为5.
        ③、Dispatcher:分发者,也就是生产者
        ④、AsyncCall:一个Runnable对象,里面封装了异步回调接口
        ⑤、ExecutorService:线程池,也就是消费者池。
        ⑥、Deque<AsyncCall> readyAsyncCalls:就绪状态的异步calls队列。
        ⑦、Deque<AsyncCall> runningAsyncCalls:正在运行的异步calls队列,包含了已经被canceled,但还没有finished的calls
        ⑧、Deque<RealCall> runningSyncCalls:正在运行的同步calls队列,包含了已经被canceled,但还没有finished的calls,之所以保存起来是为了方便进行统计正在执行的calls数目,同时在需要时统一对所有的calls执行canceled。
     
    回到异步请求源码,在Dispatcher的enqueue方法里,会判断当前正在执行的异步请求数目是否小于maxRequests 且 当前每个Host的异步请求数是否小于maxRequestsPerHost,如果两个条件都满足,则把call加入到runningAsyncCalls队列中,并调用线程池进行执行call;否则把call添加进readyAsyncCalls队列中,代码如下:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}


    以上便是OkHttp的大致执行流程。关于异步请求在线程池中是如何调用的,将在ExecutorService相关笔记中记录;关于各个拦截器的功能与工作流程、路由寻址、链路复用等,将在另外的独立笔记中记录。
    

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值