谈谈OkHttp源码的同步异步原理_enqueue会新建线程吗(2)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

} catch (IOException e) {

//请求错误时,回调错误接口给到用户
responseCallback.onFailure(RealCall.this, e);
} finally {
//结束一次请求。
client.dispatcher().finished(this);
}
}
}

AsyncCall继承于Runnable,它提供了将Call放到线程池执行的能力,实现了请求的异步流程

2.3、Dispatcher.enqueue()

//准备进行异步调用的请求。
private final Deque readyAsyncCalls = new ArrayDeque<>();
//正在执行的异步请求。
private final Deque runningAsyncCalls = new ArrayDeque<>();
void enqueue(AsyncCall call) {
synchronized (this) {
//将异步请求加入到双端队列中
readyAsyncCalls.add(call);
// 寻找是否有同Host的请求,如果有进行复用
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
//将符合条件的Ready的异步请求转入runningAsyncCalls,并执行
promoteAndExecute();
}

将此请求登记到Dispatcher的预备双端队列中。
以此次的请求的Host来查找可服用的异步请求,如果存在,进行复用。
尝试将刚刚加入预备队的请求执行

2.4、Dipatcher.finish()

private void finished(Deque calls, T call) {
Runnable idleCallback;
synchronized (this) {

//一个请求完成后,检查此时是否有在等待执行的请求,并处理。
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
//通知此时已经没有异步请求任务
idleCallback.run();
}
}

  • 调度器结束一次请求;
  • 当一个异步任务完成后,调度器会触发一次预备任务执行流程。让之前因为最大请求数等限制而不能执行的请求有机会得到执行;
  • 通过idleCallback.run()通知此时的调度器空闲状态;

2.5、Dipatcher.promoteAndExecute()

private boolean promoteAndExecute() {

List executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//检查最大请求数限制和
if (runningAsyncCalls.size() >= maxRequests) break;
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
//满足条件,便把预备队的请求提升到执行队列。
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//将可执行的异步请求放进线程池执行
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
//
asyncCall.executeOn(executorService());
}
return isRunning;

  • 该方法是对预备队列里的请求提升至执行队列并执行的一次尝试;
  • 如果不能执行,他启动时机将会延后到其他请求结束;

3、同步请求

public static final MediaType JSON
= MediaType.get(“application/json; charset=utf-8”);
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}

  • 通过Builder模式构建Request;
  • 调用client.newCall(),通过request生成一个Call对象。他的实现类是RealCall;
  • 随后调用RealCall.execute(),进行同步请求;

3.1、RealCall.execute()

@Override public Response execute() throws IOException {
synchronized (this) {
// 如果该请求已经执行过,报错。
if (executed) throw new IllegalStateException(“Already Executed”);
executed = true;
}
transmitter.timeoutEnter();
transmitter.callStart();
try {
//获取 client 里面的调度器 Dispatcher 并记录这个请求。
client.dispatcher().executed(this);
//通过责任链的方式将发起请求并返回结果。这里是同步动作,会阻塞。
return getResponseWithInterceptorChain();
} finally {
//请求完后需要把这个请求从调度器中移走
client.dispatcher().finished(this);
}
}

  • 判断Call的合法性;
  • 将RealCall传进Client里面的Dispatcher.executed()里,而Dispatcher是在 OkHttpClient 被的构建函数里被创建并作为成员变量的;
  • 开启责任链模式,进行请求相关逻辑;
  • 执行完成后,调度器对这个请求进行收尾工作;

3.2、Dispatcher.executed()

/** Running asynchronous calls. Includes canceled calls that haven’t finished yet. */
private final Deque runningAsyncCalls = new ArrayDeque<>();
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}

3.3、RealCall.getResponseWithInterceptorChain()

Response getResponseWithInterceptorChain() throws IOException {
// 将请求的具体逻辑进行分层,并采用责任链的方式进行构造。
List interceptors = new ArrayList<>();
// 用户自已的请求拦截器
interceptors.addAll(client.interceptors());
//重试和重定向拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
//桥拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存逻辑拦截器
interceptors.add(new CacheInterceptor(client.internalCache()));
//网络连接逻辑拦截器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
// 网络请求拦截器,真正网络通行的地方,这个拦截器处理过后会生成一个Response
interceptors.add(new CallServerInterceptor(forWebSocket));
//依照如上配置,构建出一个请求的处理逻辑责任链,特别注意:这条链开始于下标位置为的0拦截器。
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
//按下处理逻辑链条的开关。
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException(“Canceled”);
}
//返回请求结果
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}

  • 将用户自定义的拦截器先加入集合中;
  • 加入一次请求中需要用的拦截器,这些拦截器代表一次完整的网络请求逻辑被分了几层以及他们的先后顺序。从代码中我们不难看出他们的流程是:重试/重定向逻辑->网络桥逻辑->缓存逻辑->建立网络连接逻辑->网络通行逻辑;
  • 用以上拦截器集合构建出一条逻辑处理的拦截链,并将这条链需要使用的拦截器下标赋值为0,从第一个开始;
  • 调用chain.proceed()启动这条链的处理流程;
  • 使用责任链的设计模式来处理一次网络请求中的逻辑可以有效的划分逻辑层。而前一个拦截器可以根据实际的处理情况来决定下一拦截器是否应该继续处理;

3.4、RealInterceptorChain.proceed()

RealInterceptorChain.java
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;

/**

  • index+1:构建出新的拦截链,不过新的拦截链的处理拦截器是下标为index+1的
  • 实现了责任链中,处理逻辑的流转。
    */
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
    index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    //此时index = 0;所以拿到了第一个拦截器,并且调用他的intercept 方法进行具体逻辑处理。
    Interceptor interceptor = interceptors.get(index);
    //当前拦截器对网络请求进行处理。
    Response response = interceptor.intercept(next);
    // Confirm that the next interceptor made its required call to chain.proceed().
    if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
    throw new IllegalStateException("network interceptor " + interceptor
  • " must call proceed() exactly once");
    }
    // 省略对response合法性的检查代码

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

;
}
// 省略对response合法性的检查代码

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-sa6qWEj5-1713640372745)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值