oynix

于无声处听惊雷,于无色处见繁花

OkHttp使用

说到网络请求框架,OkHttp应该是当下较为流行的了,原因就在于它简单易用,且高效。同样,也是Square公司的作品。

1. 引入

库地址
添加依赖时,在app的build.gradle加一个库即可

1
implementation("com.squareup.okhttp3:okhttp:4.9.3")

2. 使用

使用OkHttp发送网络请求,一般分为5个步骤

  • 生成OkHttpClient
    Client可以配置一些和连接相关的参数,比如连接timeout、读timeout、写timeout、连接池的配置、添加拦截器,等等
  • 生成Request
    Request可以设定和请求相关的参数,如请求的URL、请求方式、请求头、请求的body
  • 生成Call
    一个Call,即为一个网络请求,这一步需要上面的Client和Request
  • 发送Call
    将请求发出,这里有两种方式,一种是同步发送,会阻塞线程,等待数据返回;另一种是异步发送,在回调里处理返回的数据
  • 等待Call返回Response
    Response即为服务器返回的数据,包含Header,和Body
    1
    2
    3
    4
    5
    6
    7
    OkHttpClient client = new OkHttpClient();
    Request requst = new Request.Builder().url("").get().build();
    Call call = client.newCall(request);
    Response response = call.execute(); // 同步
    call.enqueue(callback); // 异步

    String message = response.body().string();
    其中,Client也可以定制化,和Request类似,也是使用构建者模式
    1
    2
    3
    4
    5
    6
    OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(60, TimeUnit.SECONDS)
    .readTimeout(60, TimeUnit.SECONDS)
    .writeTime(60, TimeUnit.SECONDS)
    .connectionPool(new ConnectionPool(30, 30, TimeUnit.MINUTES))
    .build();

3. 责任链

责任链是OkHttp中最巧妙的设计了。链上的每个元素,抽象成了一个拦截器Interceptor,每个拦截器都可以处理Request和Response。这种设计的好处就是,将处理者和请求者解耦,缺点就是,每次发起请求需要对链上的拦截者遍历,当拦截者过多时会影响性能。

什么意思呢,简单说就是,每次发起网络请求,Request会经过责任链上的每一个拦截器,最终到达服务器,同样,服务器返回的Response也会经过链上的每一个拦截器,最终回到请求者。比如,你想给每个请求都加上一个包含鉴权的Header,那么只需要向责任链中插入一个拦截器,这样就可以触碰到所有的Request和Response,不需要操作Response,而只需要向Resquest的Header中增加一对Key-Value即可,这种模式下,不需要关心谁发起的请求,也不需要关心谁将会处理请求,只需要插入一个拦截器Interceptor即可。

这里面有两个角色,一个是链,一个是拦截器。

先说拦截器。拦截器本身是拿不到任何东西的,它只有插入到责任链上之后,才能操作Request和Response,所以,它的Request是链提供给它的,同时,它还要返回一个Response,才能保证责任链能完整的流通,不然其中某个拦截器,在拿到Request后,不返回Response,那么,请求到这里就断掉了。一个链上可以有多个拦截器,Interceptor是个接口,其中只有一个interceptor方法,自定义的拦截器需要实现这个方法,Chain提供了多个方法,来提供和请求相关的参数,比如连接timeout、读写timeout,当然,还包括Request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
}

public class HeaderInterceptor implements Interceptor {
Response intercept(Chain chain) throws IOException {
// 通过request方法获得Request
Request request = chain.request();
// do something with request
...

// 通过proceed方法得到Resposne
Response response = chain.proceed(request);
// do something with resposne
...

// 最终将response返回给链
return response;
}
}

// 将拦截器插入到链中
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor())
.build();

看完拦截器,再来看看另外一个角色,链。Chain也是一个接口,它只有一个实现类,RealInterceptorChain,看看request方法是如何获取到Request,proceed方法又是如何获取到Response的。

1
2
3
@Override public Request request() {
return request;
}

request方法很简单,就是直接返回的成员变量request,而这个变量是在构造方法里面传入的,就没什么可多说的了。那就再看看proceed方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();

calls++;

// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}

// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}

// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}

// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}

if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}

return response;
}

里面做了一些检查和判断,关键的就是中间的创建next变量。next的类型还是RealInterceptorChain,构造方法中第一个参数就是所有的拦截器,还有个index参数,表明这个next用的是第几个拦截器,换句话说就是,每个拦截器都对应一个RealInterceptorChain类型的变量。初始化next,又获取到了拦截器,通过interceptor.intercept(next)来获取response,来返回给上面的chain.proceed(request)。

可能有点乱,来捋一捋。一个拦截器想要获取Request很简单,Chain的构造方法里便有。一拦截器想要获取Response时,Chain就会获取到链中的下一个拦截器,通过调用它的intercept方法来获取Response,进入到下一个拦截器的intercept方法中时,它的Response又要通过Chain获取下下一个拦截器,跟它要Response。形象点来说就是,A想要操作Response,它就会带着Request和B要Response,B又会带着Request和C要Response,C又会和D要,就这么一直在链上传递下去。那么大家都这么踢皮球一样踢来踢去,真正的Response从何而来呢?从要有个兜底的吧?没错,真有一个兜底的,它就是链上的最后一个拦截器,CallServerInterceptor。

所以,还要再看看发送请求前,都给链上添加了那些拦截器。

上面说过,发送网路请求时,最终会生成一个Call,然后将其发出,而Call是一个接口,它只有一个实现类,叫做RealCall,不管是调用的Call的同步方法execute还是异步方法enqueue,最终都会来到RealCall里的getResponseWithInterceptorChain方法,完整内容如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
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, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());

boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}

可以看到,发送请求前,除了添加我们给Client添加的拦截器,OkHttp还添加了一些自己的拦截器,用来对Request和Response做一些处理。最后一个拦截器,便是CallServerInterceptor

1
2
3
4
5
6
7
8
9
10
// This is the last interceptor in the chain. It makes a network call to the server.
public final class CallServerInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
// 构建Request
// 发送Request
// 生成Response
// 返回response
return repsonse;
}
}

它的类说明里,写的是链中的最后一个拦截器。所以她没办法再踢皮球,和别人要Response。它的intercept方法里,将上游传来的request发送出去,然后等服务器返回,将数据封装成Response,再度返回给链上游。

总结来看就是,每个拦截器中,想要操作Response,需要将自己处理过的Request传给链下游,下游接着往下传递,直到到达链末端的CallServerInterceptor,它向服务器发送请求,接收服务器的响应,然后再将Response回传给链上游,直到到达请求发送者,这个请求算作结束。

------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2022/05/00848bfa0634/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道