1. 秦子帅的博客首页
  2. Android

Okhttp3源码解析(3)-Call分析(整体流程)

前言

前面我们讲了
Okhttp的基本用法
Okhttp3源码解析(1)-OkHttpClient分析
Okhttp3源码解析(2)-Request分析

newCall分析

Call初始化

我们首先看一下在哪用到了Call:

想起来了吧?无论是get还是post请求 都要生成call对象,在上面我们发现call实例需要一个okHttpClientrequest实例 ,我们先点进Call类去看看:

我们发现Call是个接口, 并定义了一些方方法(方法含义在注释上)。
我们继续看newCal()方法

继续点击newRealCall()去:

从代码中我们发现在newRealCall()中初始化了RealCallRealCall中初始化了retryAndFollowUpInterceptor

  • client: OkHttpClient 实例
  • originalRequest : 最初的Request
  • forWebSocket :是否支持websocket通信
  • retryAndFollowUpInterceptor 从字面意思来说, 是重试和重定向拦截器 ,至于它有什么作用我们继续往下看

同步请求分析

我们点进execute()中查看:

从上面代码得知步骤:
(1).通过 synchronized 保证线程同步,判断是否已经执行过 ,如果是直接抛异常
(2). captureCallStackTrace(); 字面意思:捕获调用堆栈跟踪,我们通过源码发现里面涉及到了retryAndFollowUpInterceptor
(3). eventListener 回调CallStart()
(4). client.dispatcher().executed(this); 看到了dispatcher是不是很熟悉?之前在分析okhttpClient初始化的时候遇到了,我们点击executed()方法进去:

发现把我们传进来的realcall放到了runningSyncCalls队列中,从字面意思来说就是正在运行的同步的调用队列中,为什么说是队列呢? :

Deque即双端队列。是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,相比list增加[]运算符重载。

(5).我们回到execute()继续往下分析,剩下的代码我们提取出三行代码:

  • equesr result = getResponseWithInterceptorChain(); 生成一个Response 实例
  • eventListener.callFailed(this, e); :eventListener的callFailed回调
  • client.dispatcher().finished(this); :dispatcher实例的finished方法

不难看出,getResponseWithInterceptorChain()一定是此方法中的核心,字面意思是获取拦截器链的响应,这就明白了,就是通过拦截器链处理后返回Response

Okhttp3源码解析(3)-Call分析(整体流程)
getResponseWithInterceptorChain() 分析

从上面代码不难看出, 对最初的request做了层层拦截,每个拦截器的原理我们放在以后的章节去讲, 这里就不展开了!
这里需要强调的一下 interceptors.addAll(client.interceptors());client.interceptors() 是我们自定义的拦截器 它是在哪定义的?如何添加?我们去OkHttpClient类中发现:

Okhttp3源码解析(3)-Call分析(整体流程)

可以通过初始化okHttpClient实例 .addInterceptor的形式 添加。

异步请求分析

点击enqueue()查看:

(1).通过 synchronized 保证线程同步,判断是否已经执行过 ,如果是直接抛异常
(2). captureCallStackTrace(); 字面意思:捕获调用堆栈跟踪,我们通过源码发现里面涉及到了retryAndFollowUpInterceptor
(3). eventListener 回调CallStart()
(4). client.dispatcher().enqueue(new AsyncCall(responseCallback)); 调用了Dispatcher.enqueue()并传入了一个new AsyncCall(responseCallback)实例,点击AsyncCall查看:
AsyncCall 是RealCall的内部类!

AsyncCall继承了NamedRunnable ,我们看下NamedRunnable是什么:

原来NamedRunnable 实现了Runnable 接口 是个线程类,在run()中 添加了抽象的execute();方法,看到这里 我们应该有一个反应,那就是AsyncCall中具体的execute()应该在子线程执行
我们继续分析,client.dispatcher().enqueue(new AsyncCall(responseCallback)); 点击进入enqueue():

  • runningAsyncCalls 正在运行的异步请求的队列
  • maxRequests 最大的请求数 64
  • maxRequestsPerHost host最大请求数 5 (可以通过Get与Set方式自定义设置)

如果正在运行的异步请求的队列大小低于64并且 正在请求的host数量低于5,就会执行(满足条件)

这里把 AsyncCall实例添加到 runningAsyncCalls中。
ExecutorService 表示线程池 继续看 executorService()

其实就是生成了executorService 实例,这就明白了,AsyncCall实例放入线程池中执行了!

如果不满足上面的请求数等条件:

就会被添加到一个等待就绪的异步请求队列中,目的是什么呢??? 当然是等待时机再次添加到runningAsyncCalls中并放入线程池中执行,这块逻辑在 AsyncCall类中的 execute() 至于原因我们继续往下看!

Okhttp3源码解析(3)-Call分析(整体流程)

刚才我们说了,如果条件满足, AsyncCall实例就会在线程池中执行(.start),那我们直接去看run()中的 execute()

上面代码中得知, 首先通过层层拦截器链处理生成了response;然后通过一系列的判断,responseCallback进行onResponseonFailure回调,最后调用的Dispatcher.finifshed()
这里需要注意的是 这里的Dispatcher.finifshed(this)与同步中的Dispatcher.finifshed(this)不一样 参数不同。

我们继续看具体的finifshed()方法:

在线程同步的情况下 执行了promoteCalls();

经过一系列的判断, 对等待就绪的异步队列进行遍历,生成对应的AsyncCall实例,并添加到runningAsyncCalls中,最后放入到线程池中执行! 这里就是我们上面说到的等待就绪的异步队列如何与runningAsyncCalls对接的逻辑。

总结

同步请求流程:
  • 生成call实例realcall
  • Dispatcher.executed()中的runningSyncCalls 添加realcall到此队列中
  • 通过 getResponseWithInterceptorChain() 对request层层拦截,生成Response
  • 通过Dispatcher.finished(),把call实例从队列中移除,返回最终的response
异步请求流程:
  • 生成一个AsyncCall(responseCallback)实例(实现了Runnable)
  • AsyncCall实例放入了Dispatcher.enqueue()中,并判断maxRequests (最大请求数)maxRequestsPerHost(最大host请求数)是否满足条件,如果满足就把AsyncCall添加到runningAsyncCalls中,并放入线程池中执行;如果条件不满足,就添加到等待就绪的异步队列,当那些满足的条件的执行时 ,在Dispatcher.finifshed(this)中的promoteCalls();方法中 对等待就绪的异步队列进行遍历,生成对应的AsyncCall实例,并添加到runningAsyncCalls中,最后放入到线程池中执行,一直到所有请求都结束。

至此OKhttp整体流程就分析完了, 下一篇会分块去分析,希望对大家有所帮助…

Okhttp3源码解析(3)-Call分析(整体流程)

大家可以关注我的微信公众号:「秦子帅」一个有质量、有态度的公众号!

Okhttp3源码解析(3)-Call分析(整体流程)
公众号

原文始发于:Okhttp3源码解析(3)-Call分析(整体流程)

发布者:秦子帅,转转请注明出处:http://qinzishuai.cn/index.php/2019/08/17/aa2b8c15f4/

联系我们

912241847

在线咨询:点击这里给我发消息

邮件:qzs531156@163.com

QR code