1:OKhttp3简介:
Okhttp3是一个支持Http和Http2的java或者Android网络请求SDK.依赖于okio.okio相对于java的IO流更高效的.
2:核心:
(1)采用责任链方式的拦截器,实现分成处理网络请求,让用户对网络请求实现了更好的拓展
(2)采用GZIP处理下载数据,压缩了数据的大小.
(3)支持http缓存
(4)采用线程池(thread pool)和连接池(Socket pool)解决多并发问题,同时连接池支持多路复用(http2才支持,可以让一个Socket同时发送多个网络请求,内部自动维持顺序.相比http只能一个一个发送,更能减少创建开销))
(5)底层采用socket和服务器进行连接.
(6)采用okio实现高效的io流读写
3:拦截器
(1)作用
拦截器Interceptor是一种强大的机制,可以监视,重写和重试网络请求.主要作用就是:在把请求发送出去之前,可以对reqeust 进行重写,在应用拿到response之前,先获得response,对其中某些数据进行监控,在有必要的情况下,对response的某些内容(比如response的header,body,response内的request的header,body)进行更改。
(2)分类:
总体上来书okhttp3 可以分为2大类:
1. 系统拦截器(核心拦截器)
1:RetryAndFollowUpInterceptor 重定向拦截器:
Okhttp内置的第一个拦截器,通过while(true)的死循环来进行对异常结果或响应结果判断是否要进行重新请求.负责失败重连和重定向的拦截器.
2:BridgeInterceptor 桥拦截器:
它可以对网络文件的类型,网页的编码和返回的数据进行解压处理.是为用户构建的一个Request请求转化为能够进行网络访问的请求,同时将网络请求回来的响应Response转化为用户可用的Response.
3:CacheIntercepor 缓存拦截器:
可以根据OkhttpClient对象的配置以及缓存策略对请求值进行缓存.
4:ConnectIntercepor :
在Okhttp底层是通过socket的方式于服务端进行连接的,并且在连接建立之后会通过OKIO获取通向server端的输入流Source和输出流Sink.
5:CallServerInterceptor :
CallServerInterceptor 在 ConnectInterceptor 拦截器的功能就是负责与服务器建立 Socket 连接,并且创建了一个 HttpStream 它包括通向服务器的输入流和输出流。而接下来的 CallServerInterceptor 拦截器的功能使用 HttpStream 与服务器进行数据的读写操作的。
2. 自定义拦截器:
1. 应用拦截器 Application Interceptor
2. 网络拦截器 NetWork Interceptor
相同点:
(1)都能对server返回的response进行拦截
(2)都基于Interceptor接口,由开发者实现这个接口,然后将自定义的Interceptor类的对象设置到okhttpclient对象中.
(3)两者都会被add到okhttpclient内的一个ArrayList中.当请求执行的时候,多个拦截器依次执行.
不同点:
(1)okhttpclient添加两种拦截器的api不同,应用拦截器的api是addInterceptor(),而添加网络拦截器的接口是addNetWorkInterceptor();
(2)两者负责的区域不同,应用拦截器作用于okhttpCore到Application之间,网络拦截器作用于newwork和okhttpCore之间.
(3)在某些情况下网络拦截器有可能被执行多次,但application只会被执行一次.
3:ok请求网络数据要走的步骤:
1:首先,在ok执行一个异步请求,new OkhttpClient()返回了一个Okhttpclient对象, 然后new Request.Builder()返回一个request对象,okhttpclient调用newCall()方法,传入request对象,然后返回一个call对象,这个call对象是call的子类RealCall调用newRealCall()方法创建出来的,这个call就是真正发起网络请求的对象.
2:RealCall(call)调用enqueue()方法,传入一个CallBack回调.RealCall对象重写了父类Call的enqueue方法
3:创建RealCall对象时通过构造传入了一个okhttpclient对象,在RealCall的enqueue()方法里,client调用了dispatcher()返回一个Dispatch分发器对象,这个类中创建了一个线程池和几个队列,以及一些参数,在这里我介绍一下:
(1):readyAsyncCalls:待执行异步任务队列
(2).runningAsyncCalls:运行中异步任务队列
(3).runningSyncCalls:运行中同步任务队列
(4).executorService:任务队列线程池
(5) int maxRequests =64
最大请求并发数为64
(6)int maxRequestsPerHost =5
'baseUrl一样的请求(域名相同),okhttp最多支持5个.
注意,此线程池的核心线程数为0,最大线程数为Integer.MAX_VALUE,线程空闲时间只能活60秒,然后用了SynchronizeQueue队列,这是一个不存储元素的阻塞队列,也就是说有任务到达的时候,只要没有空闲线程,就会创建一个新的线程来执行任务.
有了Dispatch分发器之后,通过Disapther分发器,把这次请求封装成一个AsyncCall对象,然后入队.AsyncCall里面包含了这次真正的请求RealCall,和回调CallBack.
4 : Dispatch分发器调用enqueue(),传入AsyncCall对象,然后通过readyAsyncCall.add(call)将AysncCall对象添加到等待队列,如果本次请求不是websocket,那么去正在执行的队列和等待队列中找之前的请求中是否存在host 一样的异步请求(注意是异步请求,同步请求不包括),先找正在执行的,找到了直接返回,正在执行的队列里面找不到再去等待队列中找,找到了直接返回。如果两个队列都找不到返回null,返回null 说明这个host的 请求时第一次发送。如果找到了,那么把之前请求的计数器 callsPerHost 的引用赋值个当期请求,这样才能保证所有host 一样的请求都用同一个计数器。
只有其中某一个进入执行队列开始执行就加一 asyncCall.callsPerHost().incrementAndGet(),
一旦执行完成就减一 call.callsPerHost().decrementAndGet(),该方法在Dispatcher的 finish 方法被调用,然后调用promoteAndExecute()方法.
5:在promoteAndExecute()方法中,第一步创建一个 executableCalls 空的Arraylist,用于后面遍历等待队列中任务是,把满足条件的任务加入到数组中,然后统一交给线程池去执行。
1. 循环遍历等待队列中的任务,如果当前正在执行的任务数量大于maxRequests 64,那么直接跳循环,executableCalls 元素为空,所以不做任何操作,相当于这次请求被加入到等待队列,到此结束。知道有任务完成后在Dispatcher.finish 方法中会再次调用promoteAndExecute方法。达到轮训效果。
2. 如果当前正在执行的任务数量不大于64,那么继续第二个判断,判断目前正在执行的任务中,和当前请求的host 相同的任务数量是否超过5个,超过5个就跳过该循环,继续下个循环。如果不超过就从等待对队列移除,并且让计数器加一,同时加入到executableCalls 中
3. 如果executableCalls 不为空,循环遍历,通过asyncCall.executeOn(executableCalls )把任务交给线程池去执行。
6:在executeOn()方法中,线程池调用executorService.execute(this);去执行当前任务,它会让线程去调用AsyncCall的execute()方法,然后通过链式调用拦截器,开始真正进行网络请求.Response response = getResponseWithInterceptorChain();
7:getResponseWithInterceptorChain()方法里,new了一个拦截器的集合,依次存入五个拦截器.然后通过if (!forWebSocket)判断是否是网络连接,如果是就在集合里存入networkInterceptors拦截器,如果是,就存入new CallServerInterceptor(forWebSocket)拦截器,然后new 一个拦截器链,传入拦截器集合,返回一个Interceptor.chain对象,然后调用proceed()方法,真正的将请求request转化为response,返回给getResponseWithInterceptorChain()方法
8:获得response对象之后,将结果通过responseCallBack传过去,不管是否成功,Dispatcher对象调用finished方法,对之前的计数器计数减一,这样的话,之前因为相同host 最大并发不能超过5而等待的异步任务就没有机会执行了。
9:finished 方法中又调用了重载的 finished(runningAsyncCalls, call),该方法中调用了上面5步中的promoteAndExecute(),继续去等待队列里面取等待执行的任务然后交给线程池去执行。