上面一篇文章简单介绍了拦截器,源码中拦截器的种类及作用,以及自定义拦截器和注册自定义拦截器
上篇已经讲了源码中拦截器的执行顺序
这篇主要从源码角度分析
okhttp GitHub关于拦截器的介绍
链接:https://github.com/square/okhttp/wiki/Interceptors
如果你有看这个链接,或看其他人的文章肯定很疑惑为啥
会说NetworkInterceptor走两次
因为他们都没说详细的原因,来别急,看了下面的例子就会清楚了
Interceptor 实例看结果
可以先忽略小标题看代码就好
下面分别是自定义的两种拦截器,应用拦截器和网络拦截器,来测试一下看结果
public class AppInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Log.e("Interceptor", "app intercept:begin ");
Request request = chain.request();
Response response = chain.proceed(request);//请求
Log.e("Interceptor", "app intercept:end; " + request.url()+", response ," +
response.request().url()+" code: " + response.code());
return response;
}
}
public class NetworkInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Log.e("Interceptor","network interceptor:begin");
Request request = chain.request();
Response response = chain.proceed(request);//请求
Log.e("Interceptor","network interceptor:end;" + request.url()+", response ," +
response.request().url()+" code: " + response.code());
return response;
}
}
发起网络请求
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(new AppInterceptor())
.addNetworkInterceptor(new NetworkInterceptor())
.build();
Request request = new Request.Builder().url("http://www.baidu.com/").build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(okhttp3.Call call, IOException e) {
LogUtils.d("Interceptor", "--" + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
LogUtils.d("Interceptor", "--" + response.toString());
}
});
没有发生重定向结果
10-08 01:04:24.435 20080-20301/com.qgg.practice E/Interceptor: app intercept:begin
10-08 01:04:24.477 20080-20301/com.qgg.practice E/Interceptor: network interceptor:begin
10-08 01:04:24.494 20080-20301/com.qgg.practice E/Interceptor: network interceptor:end;http://www.baidu.com/, response ,http://www.baidu.com/ code: 200
10-08 01:04:24.496 20080-20301/com.qgg.practice E/Interceptor: app intercept:end; http://www.baidu.com/, response ,http://www.baidu.com/ code: 200
10-08 01:04:24.497 20080-20301/com.qgg.practice D/Interceptor: --Response{protocol=http/1.1, code=200, message=OK, url=http://www.baidu.com/}
发生重定向结果
如果你把上面的地址改为 http://www.baidu.cn
Request request = new Request.Builder().url("http://www.baidu.cn").build();
10-08 00:56:59.149 19078-19142/com.qgg.practice E/Interceptor: app intercept:begin
10-08 00:56:59.181 19078-19142/com.qgg.practice E/Interceptor: network interceptor:begin
10-08 00:56:59.198 19078-19142/com.qgg.practice E/Interceptor: network interceptor:end;http://www.baidu.cn/, response ,http://www.baidu.cn/ code: 302
10-08 00:56:59.212 19078-19142/com.qgg.practice E/Interceptor: network interceptor:begin
10-08 00:56:59.222 19078-19142/com.qgg.practice E/Interceptor: network interceptor:end;http://www.baidu.com/, response ,http://www.baidu.com/ code: 200
10-08 00:56:59.225 19078-19142/com.qgg.practice E/Interceptor: app intercept:end; http://www.baidu.cn/, response ,http://www.baidu.com/ code: 200
10-08 00:56:59.225 19078-19142/com.qgg.practice D/Interceptor: --Response{protocol=http/1.1, code=200, message=OK, url=http://www.baidu.com/}
咦,确实,网络拦截器竟然走了两次
其实标题已经说明了原因,对,就是因为发生了重定向,可以看到之前有返回httpcode 302
这时第一类拦截器就起了作用 RetryAndFollowUpInterceptor 帮我们访问了http://www.baidu.com/
RetryAndFollowUpInterceptor 详细分析的时候再来说明
现在就可以把官网的两种拦截器的详细对比贴出来了
Application and Network interceptors 该如何选择
两个interceptor都有他们各自的优缺点:
Application Interceptors
- 不需要关心由重定向、重试请求等造成的中间response产物。
- 总会被调用一次,即使HTTP response是从缓存(cache)中获取到的。
- 关注原始的request,而不关心注入的headers,比如If-None-Match。
- interceptor可以被取消调用,不调用Chain.proceed()。
- interceptor可以重试和多次调用Chain.proceed()。
Network Interceptors
- 可以操作由重定向、重试请求等造成的中间response产物。
- 如果是从缓存中获取cached responses ,导致中断了network,是不会调用这个interceptor的。
- 数据在整个network过程中都可以通过Network Interceptors监听。
-
可以获取携带了request的Connection。
结合图和例子,再看上面的两种区别分析应该理解的比较清楚了
Interceptor 源码分析
可以看到源码设计的很巧妙 Interceptor 是接口
所以,可以肯定的是之前介绍的源码中的五种拦截器都是实现了Interceptor 接口
Interceptor 中只有一个方法intercept 参数为Chain,返回值为Response
而Chain又是一个接口
/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
*/
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
未完待续...