概述
OkHttp这个框架,由 Square公司开发,在 android app领域,制霸级别的存在。Retrofit+okHttp几乎成了移动开发中网络框架的标配。
本文分析的okhttp是基于 3.10.0的版本。
基本使用
以异步请求为例:
OkHttpClient client = new OkHttpClient(); // 创建新的client
// 或者 利用工厂类创建一个定制化的OkHttp
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
builder.connectTimeOut(60,TimeUnit.SECOND) // 设置超时时间
.addInterceptor(interceptor) // 添加拦截器
.proxy(proxy) // 设置请求代理
.cache(cache); // 设置缓存策略
client = clientBuilder.builder();
// 建造者模式,将参数设置步骤和对象创建步骤的创建分离,避免了超级多个构造函数重载的尴尬现象
Request request = new Request.Builder()
.url(url)
.build();
// 这是一个异步请求的用法。
// 请求创建出来之后,先进入到执行队列
client.newCall(request).enqueue(new Callback(){
@Override
public void onFailure(Call call,IOException e){
}
@Override
public void onResponse(Call call,Response response) throws IOException {
}
});
请求流程分析
操作的起点是:client.newCall(request).enqueue() 将异步请求加入执行队列
加入队列的具体代码为:
client.dispatcher().enqueue(new AsyncCall)
Dispatcher为 请求调度器,它是一种门户模式,所有的请求,都必须经过这唯一的门户才能执行,或者取消操作。 它内部维护了一个线程池去执行异步操作,保证了最大并发数,以及同一个host允许执行请求的线程个数。
enqueue的具体实现如下:
仔细观察这段代码会发现:
- 存在两个队列,一个running队列,一个 ready队列,前者的优先级比后者高
- 当running队列没满的时候,并且同一个host的执行线程数没超标的时候,请求都会进入到 running队列
- running队列满了,或者 同一个host执行线程数超标了,请求会进入到 ready队列
- 如果成功进入了running队列,那么 就会 使用线程池 executorService,启动一个子线程去执行它,执行的自然就是 Call的run()方法。
以下是 AsyncCall的 源代码:
图中1处,是run方法中另外调用了一个execute方法。 图中2处,是 执行网络请求的真实代码。
以下是 getResponseWithInterceptorChain()的源代码:
此处分析一下:
- 这是按照责任链模式设计的拦截器链条。
- 拦截器的执行顺序为:
- 用户自定义的拦截器
- 重试和重定向拦截器
- 在发送失败时,尝试重新发送请求。
- 当请求需要重定向时,将它重新指向另一个地址
- 桥接拦截器
- 对request header设置默认值,比如 content-type,keep-alive,cookie等
- 缓存拦截器
- 负责http请求的缓存处理
- 网络连接拦截器
- 负责与服务器建立TCP连接
- 用户自定义的网络拦截器
- 服务调用拦截器
- 调用底层硬件,向服务器发送request,并拿到response
可以看到一共有7个拦截器,其中两个是用户自定义的,暂时不关注。
下面重点分析其中的 缓存拦截器 和 服务调用拦截器。
缓存拦截器
- 根据request获取已有缓存的response,并根据缓存情况创建CacheStrategy对象来判断缓存是否有效(是否过期等)。
- 如果缓存无效,则继续调用
chain.proceed();将流程转到下一个拦截器
- 如果从服务器成功获取到了response,判断是否将此response进行缓存
Okhttp只是设计了一套缓存策略,具体如何将响应缓存到本地,并且如何从本地缓存中获取响应,都是由开发者自己决定,并通过 okhttp.builderer.cache()方法设置。
okhttp提供了一个默认的Cache类,我们可以通过下面的方式设置缓存区域的最大size。
Cache类的内部则使用了 DiskLruCache来实现具体的缓存功能。
服务调用拦截器
它是 okhttp的最后一个拦截器,是真正执行网络请求的底层核心代码,包括发出请求,以及接收回应。
okhttp使用扩展
okio是Square为okhttp打造的io流。 在构建response时,需要传入一个 ResponseBody对象。 ResponseBody内封装了对请求结果的读取操作。可以通过继承并扩展ResponseBody的方式来获取网络请求的进度。(这个还是挺有用的)
如下图,自定义一个ResponseBody,通过它可以向上层汇报网络请求的进度。
下面的代码:
- 指定了响应的缓存位置,以及缓存的空间大小
- 添加了一个自定义的网络拦截器,将进度值向外反馈
扩展实践
Picasso 也是 Square公司的一个图片加载框架,实际上它的内部还是 okhttp在请求图片数据,
要实现在图片加载时显示原型进度:
总结
OkHttp框架中使用的设计模式有
- builder建造者模式
- 避免了大量的构造函数重载
- dispatcher门户模式
- 使用统一的入口来接收所有的请求,并进行分发
- chain责任链模式
- 链式调用各个拦截器的interceptor方法,并支持添加自定义拦截器逻辑