剑客
关注科技互联网

Okhttp的使用技巧与拦截器的使用分析

Okhttp的使用技巧与拦截器的使用分析

Okhttp的结构从Call这个接口就可以大概知道了总的结构:

/**
* A call is a request that has been prepared for execution. A call can be canceled. As this object
* represents a single request/response pair (stream), it cannot be executed twice.
*
* 代表了一个单独的请求,可以被取消,不能被执行2次。
*
*/
public interface Call extends Cloneable {

Request request()
Response execute() throws IOException
void enqueue(Callback responseCallback)
void cancel()
boolean isExecuted()
boolean isCanceled()

interface Factory {
Call newCall(Request request);
}
}

Call内部有个工厂接口,只有一个newCall()方法,由OkhttpClient实现,Call的真正实现是RealCall。

Okhttp的整个调用过程:创建一个OkhttpClient对象,调用newCall方法传入一个request对象产生一个Call对象,调用call的execute或者enqueue方法获取Response对象。

同步Get请求

	String runSync(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
```
### 异步Get请求

```sh
void runAsync(String url){
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new LoggerInterceptor())
.addNetworkInterceptor(new MyNetworkInterceptor())
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("Accept", "application/json")
.addHeader("Content-Type", "application/json")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.toString());
}

@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(TAG, "onResponse: " + response.body().string());
}
});
}
```

Okhttp大量使用builder设计模式

### Request类
每一个http请求包含URL, request method(GET/POST等等),headers, requestBody.
Request通过Request.Builder对象设置各种属性,最终调用build()生成Request对象。

### Response类
Response同样是Builder结构
包含了response code(200, 401等等), headers ,response body。
(The response answers the request with a code (like 200 for success or 404 for not found), headers, and its own optional body.)


### OkHttpClient类
OkHttpClient也是Builder结构,可以是设置connectTimout, readTimeout, writeTimout的时间,
设置Dispatcher, Dns, Cache,Interceptors, NetworkInterceptors等等

### Interceptor (拦截器:阅读了okhttp源码,个人觉得很奇妙的设计)

可以自定义Interceptor实现Rewriting Requests(重写Request), Rewriting Responses(重写Response), Retrying Requests(重试Request), Follow-up Requests等
通过OkHttpClient添加自定义Interceptor。
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new MyInterceptor())
.addNetworkInterceptor(new MyNetworkInterceptor())
.build();

Interceptor是一个接口

```sh
public interface Interceptor {
Response intercept(Chain chain) throws IOException;

interface Chain {
Request request();

Response proceed(Request request) throws IOException;

Connection connection();
}
}

发现okhttp是通过执行一个List

来完成整个网络请求的过程,

// RealCall里的getResponseWithInterceptorChain()方法,
private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(
retryAndFollowUpInterceptor.isForWebSocket()));

Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}

从上面的代码可以看出List

里Interceptor的顺序,

client.interceptors() (自定义的拦截器, 在call api的前的拦截) – > retryAndFollowUpInterceptor (实现请求重试)

  • BridgeInterceptor – > CacheInterceptor – > ConnectInterceptor(真正网络连接的实现) – > client.networkInterceptors() (自定义拦截器,请求完全的拦截)

  • CallServerInterceptor

Request在Interceptor里是怎么传递的?把interceptors传递到RealInterceptorChain里,执行了chain.proceed(originalRequest).

//RealInterceptorChain的proceed方法
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) 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.httpCodec != null && !sameConnection(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.httpCodec != 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, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);

// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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");
}

return response;
}

proceed方法里这段代码由几处的this.httpCodec != null这样的判断,httpCodec什么时候不为null? 当ConnectInterceptor执行完后就不为null了,

也就是请求已经执行了才不为null。这里对在ConnectInterceptor之后的拦截器进行了检查,request的url要一致,interceptor必须执行一次proceed()方法,否则会抛异常。

所有Interceptor的intercept()返回的Response必须不能为null。

在RealCall里的getResponseWithInterceptorChain方法里,创建了一个RealInterceptorChain对象,调用proceed(),

在proceed方法里又创建了RealInterceptorChain对象,interceptors.get(index)获取interceptor,接着调用interceptor.intercept(next)把realInterceptorChain传递下,

在interceptor的intercept()方法里又调用proceed(),明显形成了一个递归。

通过RealInterceptorChain的构造器,传递interceptors,streamAllocation,httpCodec,connection,index, request。而index每次new一个RealInterceptorChain都会加1,

是为了保证interceptors遍历。

RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, Connection connection, int index, Request request)

这样的设计可以在Interceptor的intercept()方法里调用proceed(request,streamAllocation,httpCodec, connection)

这个方法之前替换request等参数,也可以在返回Response前替换response。灵活性就大大增加。

在ConnectInterceptor之后的拦截器必须满足:request的url要一致,interceptor必须执行一次proceed()。这样子做是为了保证递推的正常运作。

而对与client.interceptors是在ConnectInterceptor之前的拦截器,可以不用必须执行一次proceed()。可以实现直接返回虚拟的response用于是测试等功能。

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址