简介
先简单介绍下Okhttp的用法吧:
直接从官网摘的:
这是同步的方式,异步就不放了。可以看到里面其实很关键的几个变量或者方法:OkHttpClient、Request、Response、newCall(),newCall其实返回的是一个Call对象,所以OK实际上也就是由这几个关键类构造起来的,为外部提供接口调用。
其他一些OK的详细用法,就不在这里讲了,这篇主要串一下OK的工作原理。
Loading…….
入口
先看下OkhttpClient这个类,它实现了Call.Factory接口,是构建call对象的关键。
Builder是OkHttpClient中的静态内部类,主要是对一些参数进行初始化,比如说dispatcher等。
|
|
OkhttpClient中的newCall方法返回了一个realCall对象。我们这里先从同步的方式入手:
|
|
再看其调用的excute()方法,先同步保证当前call对象只能被执行一次。然后调用dispatcher().executed(this),做一些准备工作。
调用getResponseWithInterceptorChain();来实际执行请求并且获取实际Http返回结果,最后调用dispatcher()通知请求结束。
runningSyncCalls是一个双向队列,存储的是正在执行的同步的RealCall对象。
核心
|
|
Interceptor 这个东西不仅仅是仅仅起了拦截请求的功能,它是OK的核心,把实际的网络请求、IO、缓存、透明压缩等功能都统一了起来,每一个功能都只是一个 Interceptor,它们再连接成一个 Interceptor.Chain,环环相扣,最终圆满完成一次网络请求。
可以看到上面代码,责任链的顺序:
- 在配置OkhttpClient时设置的interceptor(自定义的interceptor);
- 负责失败重试和重定向的拦截器;
- 负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的网络响应转换为用户响应的 BridgeInterceptor;
- 负责请求的缓存,包括读取和写入;
- 负责和服务器建立连接的 ConnectInterceptor;
- 配置OkHttpClient时设置的 networkInterceptors;
- 负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。
简单分析下:
1)这里功能虽然复杂,但是最后一个功能一定是负责和服务器进行实际通讯的,关于缓存、重定向之类都是需要在之前设置的。
2)Request通过这个责任链转换为Response, 而每个interceptor都可以独立的返回一个response,交由上层处理。
3)上层链的proceed方法 调用下层的intercept方法执行处理逻辑并继续去调用下层链的proceed,并返回response。层层嵌套,从而执行完整个责任链。这里逻辑的总控在类 RealInterceptorChain 中,大家可以下去仔细看下。
4)这种责任链模式将实际的网络请求操作逻辑给隔离,脱离RealCall,简化了各个部分负责的功能,优雅的实现了最终的需求。
其实这个责任链特别像计网中的分层,有木有! 下层向上层传递数据。
Interceptor
|
|
可以看到,建立连接其实就是根据StreamAllocation创建了一个HttpCodec对象和RealConnection对象,httpcodec主要是对http请求进行编码和对响应进行解码,HttpCodec的两个实现类Http2Codec、Http1Codec,很明显是针对Http/1.1 和Http/2版本。
StreamAllocation主要是用来协调Connections、Streams、Calls三个实体类之间的关系。大致上的逻辑就是寻找合适的realconnection,利用 RealConnection 的输入输出(BufferedSource 和 BufferedSink)创建 HttpCodec对象。
比如这样:(只截取了一小段)
再看看CallServerInterceptor
这里只挑出了核心代码,省略了一些监听回调和检查代码。
可以看到它的大致流程:
- 通过httpcodec写入request header
- 如果request body不为空,则发送到服务器
- 读取responseHeader,构建Response
- 如果response body不为空,则加入response中
这4步的核心都是由HttpCodec完成,HttpCodec的底层又是通过Okio,而Okio实际上还是用的Socket(socket逻辑实现在RealConnection中)。这里具体的逻辑就不分析了,有兴趣的童鞋可以去了解下。
经过上面的同步请求execute后,我们就能拿到返回的Response。
注意:响应的其他部分可以随意获取,但是response.body必须通过数据流的方式来进行访问(ok也提供了 body.string() 和 bytes() 这样的方法将流内的数据一次性读取完毕)。响应的 body 被封装到 ResponseBody 类中,该类主要有两点需要注意:
(1)每个 body 只能被消费一次,多次消费会抛出异常;
(2)body 必须被关闭,否则会发生资源泄漏;
异步
|
|
可以看到异步enqueue()方法主要是通过dispatcher来执行,如果队列runningAsyncCalls当前还能执行一个并发请求(具体的判断标准:即正在执行的异步队列中Call对象个数小于maxRequests(64)并且执行队列中的同一个host对应的Call对象个数小于maxRequestsPerHost(5)的时候),则调用executorService立即执行,可以看到这个executorService就是一个可缓存线程池。否则 将任务加入队列readyAsyncCalls,等待正在执行的Call请求的完成,调用promoteCalls,将call请求加入running状态中。
|
|
上面的AsyncCall 是RealCall中的一个内部类,它实现了Runnable接口,所以可以直接提交给线程池执行,这里的run方法在父类NamedRunnable 中,内部其实调用的还是子类的execute方法,因此这里获取response的逻辑还是通过getResponseWithInterceptorChain责任链来进行的,最后通过接口回调responseCallback将响应发回去。注意这里是没有做线程切换的,所以想要UI更新的童鞋注意了。
Last
本篇只是简单的介绍了ok的工作流程,里面详细的工作机制,比如socket连接池、缓存策略等,只能期待下一篇了。