1. OkHttp对于网络请求都有哪些优化
- 通过连接池来减少请求延时
- 无缝支持GZIP来减少数据流量
- 缓存响应数据来减少重复的网络请求
- 可以从很多常用的连接问题中自动恢复
2. OkHttp框架中都用到了哪些设计模式
- 外观模式
- 建造者模式(
OkHttpClient
,Request
等各种对象的创建) - 原型模式
- 责任链模式(拦截器)
- 简单工厂(CacheStrategy#Factory)
3. 为什么response.body().string() 只能调用一次
我们可能习惯在获取到Response对象后,先response.body().string()打印一遍log,再进行数据解析,却发现第二次直接抛异常,其实直接跟源码进去看就发现,通过source拿到字节流以后,直接给closeQuietly悄悄关闭了,这样第二次再去通过source读取就直接流已关闭的异常了。
public final String string() throws IOException {
BufferedSource source = source();
try {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
} finally {
//这里讲resource给悄悄close了
Util.closeQuietly(source);
}
}
4. 如何实现网络请求
OkHttp3的最底层是Socket,而不是URLConnection,它通过Platform的Class.forName()反射获得当前Runtime使用的socket库。
5. 断点续传
step 1:判断检查本地是否有下载文件,若存在,则获取已下载的文件大小 downloadLength,若不存在,那么本地已下载文件的长度为 0
step 2:获取将要下载的文件总大小(HTTP 响应头部的 content-Length)
step 3:比对已下载文件大小和将要下载的文件总大小(contentLength),判断要下载的长度
step 4:再即将发起下载请求的 HTTP 头部中添加即将下载的文件大小范围(Range: bytes = downloadLength - contentLength)
6. Dispatcher
- 记录同步任务、异步任务及等待执行的异步任务。
- 线程池管理异步任务。
- 发起/取消网络请求API:execute、enqueue、cancel。
OkHttp设置了默认的最大并发请求量 maxRequests = 64 和单个host支持的最大并发量 maxRequestsPerHost = 5。
7. RetryAndFollowUpInterceptor
负责失败自动重连和必要的重定向
- 协议问题,不能重试。
- 安全问题,不要重试。
- 如果是超时问题,并且请求没有被发送,可以重试,其他的就不要重试了。
- 没有更多的可以使用的路由,不能重试
- 如果我们在配置OkHttpClient中配置retryOnConnectionFailure属性为false,表明拒绝失败重连,那么这里返回false
8. BridgeInterceptor
-
负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应,是从应用程序代码到网络代码的桥梁
-
设置内容长度,内容编码
-
设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
-
添加cookie
-
设置其他报头,如User-Agent,Host,Keep-alive等。其中Keep-Alive是实现连接复用的必要步骤
9. CacheInterceptor
- 通过Request尝试到Cache中拿缓存(里面非常多流程),当然前提是OkHttpClient中配置了缓存,默认是不支持的。
- 根据response,time,request创建一个缓存策略,用于判断怎样使用缓存。
- 如果缓存策略中设置禁止使用网络,并且缓存又为空,则构建一个Resposne直接返回,注意返回码=504
- 缓存策略中设置不使用网络,但是有缓存,直接返回缓存
- 接着走后续过滤器的流程,chain.proceed(networkRequest)
- 当缓存存在的时候,如果网络返回的Resposne为304,则使用缓存的Resposne。
- 构建网络请求的Resposne
- 当在OKHttpClient中配置了缓存,则将这个Resposne缓存起来。
- 缓存起来的步骤也是先缓存header,再缓存body。
- 返回Resposne。
- OkHttpClient源码中支持GET形式的缓存。
10. ConnectInterceptor
1.尝试当前连接是否可以复用。
2.尝试连接池中找可以复用的连接
3.切换路由,继续在连接中尝试找可以复用的连接
4.以上都没有则new一个新的。
5.新的连接放入连接池
5.建立连接,开始握手
11. CallServerInterceptor
1.先写入请求Header
2.如果请求头的Expect: 100-continue时,只发送请求头,执行3,不然执行4
3.根据后台返回的结果判断是否继续请求流程
4.写入请求体,完成请求
5.得到响应头,构建初步响应
6.构建响应体,完成最终响应
7.返回响应
12. addInterceptor与addNetworkInterceptor的区别
二者通常的叫法为应用拦截器和网络拦截器,从整个责任链路来看,应用拦截器是最先执行的拦截器,也就是用户自己设置request属性后的原始请求,而网络拦截器位于ConnectInterceptor和CallServerInterceptor之间,此时网络链路已经准备好,只等待发送请求数据。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
网友评论