okhttp源码解析系列文章:
第一篇:《okhttp框架简单介绍》
https://blog.csdn.net/colinandroid/article/details/79774907
第二篇:《同步请求流程和源码分析》
https://blog.csdn.net/colinandroid/article/details/79774918
第三篇:《异步请求流程和源码分析》
https://blog.csdn.net/colinandroid/article/details/79774932
第四篇:《任务调度核心类dispatcher解析》
https://blog.csdn.net/colinandroid/article/details/79774936
第五篇:《拦截器流程和源码解析》
https://blog.csdn.net/colinandroid/article/details/79706161
就同步和异步请求的方法调用来说,两者的差别不是很大。执行同步请求调用的是execute()方法,执行异步请求调用的是enqueue()方法,而它们两者的内部实现原理还是有很大差别的。
经过对同步请求的分析,我们已经对okhttp网络请求的方法执行很熟悉了,并且前两步都是一样的,我们这里再简单介绍一下。
第一步,创建okHttpClient对象和Request对象;第二步,创建Call对象;第三步,执行enqueue()方法。
1. 我们来看看enqueue()方法都做了什么
这里把传进来的responseCallback对象封装成AsyncCall,然后作为参数传给okhttpclient的dispatcher对象的enqueue()方法。
这个AsyncCall又是什么呢?
我们看到AsyncCall继承自NamedRunnable,而NameRunnable实现了Runnable接口,所以这里就是把responseCallback传进Runnable中。
接着,调用okhttpclient的分发器类dispatcher的enqueue()方法。
这里首先会判断runningAsyncCalls队列的长度是否小于允许的最大请求数maxRequests,以及我们主机的最大请求数是否小于我们设定的值。
如果满足这个条件,我们就把AsyncCall对象放到runningAsyncCalls队列中,然后再通过一个线程池来执行这个异步请求。
总结一下enqueue()方法执行的流程
1. 判断当前Call是否执行过
2. 封装AsyncCall对象
3. 调用dispatcher的enqueue()方法
2.executeService().execute()执行流程
可以看到executeService()方法创建了一个线程池。我们来看下线程池的参数,第一个参数代表了核心线程的数量,这里设定为0。为什么要设定为0呢?这代表这空闲一段时间后,就会把所有线程全部销毁;第二个参数代表允许最多线程个数,这里设定为无限大,那当线程个数过多时会不会引起程序崩溃呢?我们前文提到,okhttpclient允许的最大请求是64个,它限制了okhttpclient整个异步请求数最大为64个;第三个参数代表当多余线程数大于核心线程数时,空闲的线程最多可以存活60s。
那么为什么这样设定参数呢?在我们实际运行当中可能开启20个并发请求,那么线程池因此也会创建20个线程,当工作完成后线程池就会在60s后相继消除空闲的线程。
我们接着看,这一步其实就是调用线程池的execute()方法,来执行AsyncCall的run()方法。
3. 我们来看看AsyncCall的run()方法是如何实现的
我们在AsyncCall中没有找到run()方法,所以我们去它的父类NamedRunnable中去找,我们看到它其实就是做了一层封装,最后调用的是execute()方法。我们来看看AsyncCall的execute()方法。
这里调用getResponseWithInterceptorChain()方法,通过拦截器链过滤最终得到Response对象。该方法是okhttp设计的非常精妙的一点。
Call执行完成后会从runningAsyncCalls中移除这个线程,只有这样readyAsyncCalls中的线程才能被执行。那么问题来了,我们什么时候开始去移除这个线程呢?
在finally中我们看到调用了okhttpclient的dispatcher的finish()方法。
finish()方法主要做了以下几件事:
1. 把请求从异步请求队列中删除
2. promiteCalls()方法执行
3. 如果异步请求队列为空了,则回调idleCallback()方法
可以看到异步请求的finish()方法只比同步请求的finish()方法多了一步promoteCalls()方法的执行,它其实是用来调整我们的任务队列的。我们知道异步请求需要的两个队列都是现场不安全的,所以调用promoteCalls()方法需要在同步代码块中执行。