简单使用
接入okHttp
直接在gradle中引入okHttp的依赖包
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
调用
okHttp的调用比较简单,大致分为以下几个步骤:
构建网络请求控制对象OkHttpClient
构建请求对象request
创建Call对象
创建接收返回数据的对象response
发送网络请求
okHttp的请求方式有两种,同步和异步请求。
两者在使用方式上最大的区别就是在最后一步调用方式上:
同步请求调用的是execute(),异步请求调用的是enqueue()。
同步请求示例代码
//1.构建OkHttpClient对象
OkHttpClient okHttpClient=new OkHttpClient();
//2.构建一个请求对象request,比如请求的url,请求方式
Request.Builder builder=new Request.Builder();
Request request=builder.url("http://baidu.com")
.get()
.build();
//3.创建一个Call对象
final Call call= okHttpClient.newCall(request);
new Thread(new Runnable() {
@Override
public void run() {
try {
//4.发送请求,并接收回复的返回数据
Response response=call.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
异步请求的实例代码
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
工作流程
整体流程
使用okHttpClient创建一个call,并发起同步或者异步请求后,okHttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()与enqueue()方法对同步或者异步请求进行处理。
execute()与enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChai()方法,从阻拦器链中获取返回结果。
阻拦器链中,依次通过RetryAndFollowUpInterceptor(重定向拦截器)、BridgeInterceptor(桥接拦截器)、CacheInterceptor(缓存拦截器)、ConnectInterceptor(连接拦截器)、CallServerInterceptor(网络拦截器)对请求依次解决,与服务器建立连接后,获取返回数据,再经过上述拦截器依次解析后最后将结果返回给调用方。
整体流程图
整体流程图中涉及到几个关键核心类:
OkHttpClient:整个OkHttp的核心管理类,所有的内部逻辑和对象归OkHttpClient统一管理。
RealCall:负责请求的调度(同步请求在当前线程发送,异步请求则使用OkHttp内部的线程池进行);负责构造内部逻辑责任链,并执行责任链相关的逻辑,直到获取服务器响应的结果。
由上图我们可以看出,使用execute()和enqueue()方法后,都会执行getResponseWithInterceptorChain()这个方法获取请求结果。那么我们接下来就解析下这个方法。
val interceptors = mutableListOf<Interceptor>()
// 用户自定义拦截器
interceptors += client.interceptors
// 重定向拦截器
interceptors += RetryAndFollowUpInterceptor(client)
// 桥接拦截器
interceptors += BridgeInterceptor(client.cookieJar)
// 缓存拦截器
interceptors += CacheInterceptor(client.cache)
// 连接拦截器
interceptors += ConnectInterceptor
if (!forWebSocket) {
// 网络拦截器
interceptors += client.networkInterceptors
}
// 调用服务拦截器
interceptors += CallServerInterceptor(forWebSocket)
// 创建一个拦截链条,将当前的拦截器集合传递进去
val chain = RealInterceptorChain(interceptors, transmitter, null, 0, originalRequest, this,
client.connectTimeoutMillis, client.readTimeoutMillis, client.writeTimeoutMillis)
var calledNoMoreExchanges = false
try {
// 调用链条的第一个拦截器的处理方法,并返回处理结果
val response = chain.proceed(originalRequest)
从上面的源码我们可以看出,getResponseWithInterceptorChain()生成一个list集合存放多个拦截器,然后生成一个RealInterceptorChain对象,并调用RealInterceptorChain.proceed()获取到服务器的响应结果。
RealInterceptorChain源码解析图
okHttp的核心 --- 拦截器
由上面我们知道okHttp的核心就是这些拦截器,那么我们接下来就分析这个拦截器。
根据RealInterceptorChain源码得到的流程图
结合上面的示意图,可以得到以下结论
拦截器按照添加顺序执行
拦截器的执行从RealInterceptorChain.proceed()开始,进入到第一个拦截器的执行逻辑
每个拦截器在执行之前,会将剩余尚未执行的拦截器组成新的RealInterceptorChain
拦截器的逻辑被新的责任链调用next.proceed()切分为start、next.proceed、end这三个部分依次执行
next.proceed() 所代表的其实就是剩余所有拦截器的执行逻辑
所有拦截器最终形成一个层层内嵌的嵌套结构
接下来我们再了解下每一个拦截器的作用
retryAndFollowUpInterceptor(重定向拦截器):重试那些失败或者重定向的请求,因为在整个网络请求的过程中可能出现网络不稳定的情况。
BridgeInterceptor(桥接拦截器):请求之前对响应头做了一些检查,并添加一些头。负责将用户构建好的Request请求转换成可以进行网络访问的请求。
CacheInterceptor(缓存拦截器):做一些缓存工作,获取相应时先从缓存里去获取,如果缓存没有才会进行网络请求,并把获取到响应放入缓存中。
ConnectInterceptor(连接拦截器):负责建立和目标服务器的连接。
CallServerInterceptor(发送服务请求拦截器):负责向服务器发起真正的网络请求。
下面我们就对拦截器进行详细的分析:
RetryAndFollowUpInterceptor
RetryAndFollowUpInterceptor源码逻辑图
从上图中可以看出,RetryAndFollowUpInterceptor开启了一个while(true)的循环,并在循环内部完成两个重要的判定,如图中的蓝色方框:
当请求内部抛出异常时,判定是否需要重试
当响应结果是3xx重定向时,构建新的请求并发送请求
重试的逻辑相对复杂,有如下的判定逻辑(具体代码在RetryAndFollowUpInterceptor类的recover方法):
规则1: client的retryOnConnectionFailure参数设置为false,不进行重试
规则2: 请求的body已经发出,不进行重试
规则3: 特殊的异常类型不进行重试(如ProtocolException,SSLHandshakeException等)
规则4: 没有更多的route(包含proxy和inetaddress),不进行重试
BridgeInterceptor
public final class BridgeInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
Request userRequest = chain.request();
Request.Builder requestBuilder = userRequest.newBuilder();
RequestBody body = userRequest.body();
//进行请求头的包装
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString());
}
if (contentLength != -1) {
requestBuilder.header("Content-Length", Long.toString(contentLength));
requestBuilder.removeHeader("Transfer-Encoding");
}
//....
}
responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
}
return responseBuilder.build();
}
流程:
对请求头进行封装,将Request请求转化为可以进行网络访问的请求
进行网络请求
将请求得到的响应结果Response转化为用户可见的Response
BridageInterceptor 拦截器的功能如下:
负责把用户构造的请求转换为发送到服务器的请求 、把服务器返回的响应转换为用户友好的响应,是从应用程序代码到网络代码的桥梁
设置内容长度,内容编码
设置gzip压缩,并在接收到内容后进行解压。省去了应用层处理数据解压的麻烦
添加cookie
设置其他报头,如User-Agent,Host,Keep-alive等。其中Keep-Alive是实现连接复用的必要步骤
CacheInterceptor
public final class CacheInterceptor implements Interceptor {public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null? cache.get(chain.request()): null;
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
//调用下一个拦截器
try {
networkResponse = chain.proceed(networkRequest);
} finally {
//...
}
}
...
if (cacheResponse != null) {
if (networkResponse.code() == HTTP_NOT_MODIFIED) {
Response response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis())
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close();
cache.trackConditionalCacheHit();
cache.update(cacheResponse, response);
return response;
} else {
closeQuietly(cacheResponse.body());
}
}
...
//如果缓存没有响应且网络请求结果为空
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(Util.EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build();
}
流程:
获取本地缓存cacheCandidate,如果本地缓存可用则打断interceptor链,返回cacheCandidate。
调用下一个interceptor获取networkResponse
用networkResponse、cacheResponse构造新的response
根据新的response里的header通过缓存策略存入缓存中
CacheInterceptor 拦截器的逻辑流程如下:
通过Request尝试到Cache中拿缓存,当然前提是OkHttpClient中配置了缓存,默认是不支持的。
根据response,time,request创建一个缓存策略,用于判断怎样使用缓存。
如果缓存策略中设置禁止使用网络,并且缓存又为空,则构建一个Response直接返回,注意返回码=504
缓存策略中设置不使用网络,但是又缓存,直接返回缓存
接着走后续过滤器的流程,chain.proceed(networkRequest)
当缓存存在的时候,如果网络返回的Resposne为304,则使用缓存的Resposne。
构建网络请求的Resposne
当在OkHttpClient中配置了缓存,则将这个Resposne缓存起来。
缓存起来的步骤也是先缓存header,再缓存body。
返回Resposne
ConnectInterceptor
okhttp的一大特点就是通过连接池来减小响应延迟。如果连接池中没有可用的连接,则会与服务器建立连接,并将socket的io封装到HttpStream(发送请求和接收response)中,这些都在ConnectInterceptor中完成。
public final class ConnectInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
Request request = realChain.request();
//获取之前的拦截器传过来的 StreamAllocation
StreamAllocation streamAllocation = realChain.streamAllocation();
// We need the network to satisfy this request. Possibly for validating a conditional GET.
boolean doExtensiveHealthChecks = !request.method().equals("GET");
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
//通过streamAllocation获取RealConnection对象
RealConnection connection = streamAllocation.connection();
return realChain.proceed(request, streamAllocation, httpCodec, connection);
}
}
流程:
ConnectInterceptor获取之前的拦截器传过来的 StreamAllocation
通过streamAllocation获取RealConnection对象,它是真正用于连接网络的对象
将RealConnection对象,以及对于与服务器交互最为关键的HttpCodec等对象传递给后面的拦截
CallServerInterceptor
CallServerInterceptor是最后一个拦截器,它负责发起真正的网络请求并接收服务器返回的响应数据
public final class CallServerInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
RealInterceptorChain realChain = (RealInterceptorChain) chain;
HttpCodec httpCodec = realChain.httpStream();
StreamAllocation streamAllocation = realChain.streamAllocation();
RealConnection connection = (RealConnection) realChain.connection();
Request request = realChain.request();
realChain.eventListener().requestHeadersStart(realChain.call());
//写入请求头信息
httpCodec.writeRequestHeaders(request);
realChain.eventListener().requestHeadersEnd(realChain.call(), request);
//写入请求体的信息
request.body().writeTo(bufferedRequestBody);
//完成网络请求工作
httpCodec.finishRequest();
if(responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
//读取网络响应的头信息
if (responseBuilder == null) {
realChain.eventListener().responseHeadersStart(realChain.call());
responseBuilder = httpCodec.readResponseHeaders(false);
}
//读取网络响应的body信息
if (forWebSocket && code == 101) {
// Connection is upgrading, but we need to ensure interceptors see a non-null response body.
response = response.newBuilder()
.body(Util.EMPTY_RESPONSE)
.build();
} else {
response = response.newBuilder()
.body(httpCodec.openResponseBody(response))
.build();
}
}
return response;
}
流程:
写入请求头和请求体信息,发起正在的网络请求
获取网络请求返回的响应,读取响应头和响应体的信息
返回最终的响应结果