当前位置: 代码迷 >> 综合 >> OKHTTP 源码分析(1)调用流程梳理
  详细解决方案

OKHTTP 源码分析(1)调用流程梳理

热度:70   发布时间:2023-11-17 10:33:50.0

序 、最近因为一些原因 ,会连续记录学习的一些笔记 。

 

背景

PS:OKHTTP 版本为 3.10.0 

api 'com.squareup.okhttp3:okhttp:3.10.0'

get 请求示例代码

        OkHttpClient okHttpClient = new OkHttpClient.Builder().build();final Request request = new  Request.Builder().url("www.baidu.com").get().build();Call call = okHttpClient.newCall(request);同步方法try {Response execute = call.execute();} catch (IOException e) {e.printStackTrace();}异步方法call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {String string = response.body().string();response.body().byteStream();}});

核心类 OkHttpClient 、Request 、Call 、RealCall 、Dispatcher 、Deque 、AsyncCall 、Response  。

 

流程梳理

1. OkHttpClient 网络配置层

使用构建者模式创建实例 (在后续文章会详细介绍构建者模式)。

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

下图是此构建者可以 add 的参数配置 。我们平时最常使用的比如添加拦截器操作 ,就是通过此构建者 。

2. Request 当前请求体

使用构建者模式创建实例 (在后续文章会详细介绍构建者模式)。

final Request request = new  Request.Builder().url("www.baidu.com").get().build();

3. Call 可以操作异步也可以操作同步 。

Call call = okHttpClient.newCall(request);

我们先来分析一下 Call 对象  ,直接进入 newcall 方法 。如下 ,最终返回的是 RealCall 对象 ,RealCall 是 Call 接口的实现类 。

 @Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);}

我们直接分析异步方法调用

 异步方法call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {String string = response.body().string();response.body().byteStream();}});

4. RealCall 作为 Call 的实现类 ,重写了 Call 的异步方法 enqueue ,我们直接进入 RealCall 类里面查看 enqueue 。

来源 :RealCall @Override public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);client.dispatcher().enqueue(new AsyncCall(responseCallback));}

我们先来看此方法前三行的加锁代码 ,意思是不能请求两次异步方法 。可以自己写个 demo ,异步方法在写一遍如下 。运行之后一定会报错 Already Executed 。 

异步方法call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {String string = response.body().string();response.body().byteStream();}});异步方法call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {String string = response.body().string();response.body().byteStream();}});

5. RealCall 的 enqueue 方法的最后一行代码   任务调度器 Dispatcher 。

client.dispatcher().enqueue(new AsyncCall(responseCallback));

RealCall 的 enqueue 方法最后调用的是任务调度器 Diaspatcher 的 enqueue 方法 ,会传去一个 AsyncCall 参数 。

任务调度器 Dispatcher 类synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {runningAsyncCalls.add(call);executorService().execute(call);} else {readyAsyncCalls.add(call);}}

首先看方法体里面的内容 。有几个参数 。

双端队列 :Deque 运行队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
等待队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();最大请求数量
private int maxRequests = 64;最大请求 HOST
private int maxRequestsPerHost = 5;

PS:首先我们看方法里面的判断条件 :

runningAsyncCalls.size() < maxRequests  运行队列的数量小于 64 个 ,意思是同时运行的异步任务不能超过 64个 。

 runningCallsForHost(call) < maxRequestsPerHost 不能同时访问 5 个 Host端口 。

如果满足以上条件 ,则会被添加到 runningAsyncCalls 队列 。然后在执行 ,否则的话 ,就会被加入等待队列 readyAsyncCalls.add(call) 。

6. 任务调度器传入一个 AsyncCall ,我们来看一下这个类 。

点进去发现 AsyncCall 是 RealCall 的一个内部类 。实现了 NameRunnable 接口 ,接口有实现了 Runnable 。作为一个线程 ,重写 Run 方法 ,并且调用抽象方法 execute 。所以我们来看 AsyncCall 重写的方法 execute 方法 。

RealCall 的内部类 AsyncCall 重写的抽象方法 @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);}}
Response response = getResponseWithInterceptorChain();

使用责任链模式返回 Response 。这个之后在详细写写 ,在此跳过 。

PS:我们来看一下这个 try catch 。需要注意一个变量  signalledCallback ,在 try 里面无论是走成功还是失败的回调 ,都会把这个变量设置为 true 。然后在 catch 里面 如何为 true 的话 ,就直接打印说是你用户的错 ,跟OKHTTP 无关 ,符合程序员的形象 ,不甩锅也不替别人背锅 。

解释一下 。

如果 是 这行代码 Response response = getResponseWithInterceptorChain(); 发生异常 ,则直接在catch里面走失败回调 。如果已经走过成功失败回调 ,证明 response 是没有问题的 我们可以看下异步回到的方法 。

 //异步方法call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {String string = response.body().string();response.body().byteStream();}});

在 onResponse 回调中国如果因为我们自己的代码逻辑错误抛出异常 ,则回到 execute 方法中就会到 catch 中 ,然后变量signalledCallback 告诉你这是你自己的问题,不是 OKHTTP 的问题 。

 

OKOK END 、

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  相关解决方案