OkHttp初识拦截器

引言

简单回顾同步 / 异步:

    同步请求就是执行请求的操作是阻塞式,直到HTTP响应返回。

    异步请求就类似于非阻塞式的请求,它的执行结果一般都是通过接口回调的方式告知调用者。

OkHttp拦截器:

来自官网的解释:拦截器是 OkHttp 中提供一种强大的机制,它可以实现网络监听、请求以及响应重写、请求失败重试等功能。

拦截器是OkHttp 执行网络请求中的重要角色,贯穿了整个请求执行的过程。(注:okhttp2.2以后才有拦截器的概念,2.2以后经过了一次代码重构,加入了拦截器机制)

为了了解拦截器,阅读 官方文档是必不可少的步骤,地址如下:https://github.com/square/okhttp/wiki/Interceptors

本文主要将官方文档中的重要概念,以及重要段落的翻译一一写明,可能有翻译的不周到的地方,见谅。

重要概念

1)拦截器的地位

下图是Okhttp工作流程图
Okhttp工作流程图
可以看出,Interceptor 贯穿了整个请求过程,是在请求执行过程中扮演重要角色。

这是 OkHttp 的请求执行过程,从应用发出 request,到应用收到 response,期间经历了N个拦截器。

2)拦截器的分类

OkHttp 包含 Application Interceptors【应用拦截器】、系统拦截器、Network Interceptors【网络拦截器】;

Okhttp工作流程图中,橙色框框内的那些拦截器,属于okhttp库内部定义的,一般情况下不会更改。所以这里只讨论开发者能够自定义的拦截器。

分为两类:

    1)ApplicationInterceptor(应用拦截器)

    2)NetworkInterceptor(网络拦截器)

他们的异同:

相同点:

    1)都能对server返回的response进行拦截(好像是废话···)

    2)这两种拦截器本质上都是基于Interceptor接口,由开发者实现这个接口,然后将自定义的Interceptor类的对象设置到okhttpClient对象中(参见上面的Main2Activity代码)。所以,他们的对象,本质上没什么不同,都是Interceptor的实现类的对象。

    3)两者都会被add到OkHttpClient内的一个ArrayList中。当请求执行的时候,多个拦截器会依次执行(list本身就是有序的)。

不同点:

    1)okhttpClient添加两种拦截器的api不同。添加应用拦截器的接口是addInterceptor(),而添加网络拦截器的接口是addNetworkInterceptor().

    2)两者负责的区域不同,从最上方图中可以看出,应用拦截器作用于okhttpCore到Application之间,网络拦截器作用于 network和okhttpCore之间

    3)在某种特殊情况下(比如:访问了一个url,结果这个url发生了重定向),网络拦截器有可能被执行多次,但是 不论任何情况,application只会被执行一次。(这个,证明起来十分简单,只需要将上面代码中30行的addInterceptor改成addNetworkInterceptor,运行起来再观察日志打印,就会发现,内容被打印了两次,我就不再试了,有兴趣的可自己运行代码)

接下来要分析的就是从系统自带的拦截器开始,那系统拦截器都有哪些呢,如下图:
系统拦截器
关于这些系统拦截器在接下来会一一进行详细分析的,目前先有个大致的印象。

3)拦截器链介绍:

有了上面的理论了解之后,下面则从源码的角度来对拦截器进行一个进一步了解,这里以同步请求为例,因为异步请求关于拦截器这块基本上一样:
在这里插入图片描述Response execute()
进一步定位:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那接下来将注意力集中在RealInterceptorChain.proceed()方法中:
在这里插入图片描述
然后此时在这个方法中有个核心代码:
在这里插入图片描述
这样就将若干个拦截器以链式的方式进行执行,这也是拦截器链的主要作用。下面总结一下拦截器:

  1. 创建一系列的拦截器,并将其放入到一个拦截器list中。
  2. 创建一个拦截器链RealInterceptorChain,并执行拦截器链的proceed方法。

那目前对于拦截器怎么链起来的有了一个大局观,那到底每个拦截器是怎么来衔接的呢?这里以一个具体的拦载器为例初步进行分析:RetryAndFollowUpInterceptor:
在这里插入图片描述
所以将目光集中在这个方法之上,代码比较多,这里先只看核心的:
在这里插入图片描述
而如之前所分析,我们知道RealInterceptorChain在proceed()方法中又会去获取下一个拦截器继续往下链,如下:
在这里插入图片描述
如此循环,就会将所有的拦截器依顺序一个个去执行完成,这也就是拦截器的一个核心实现逻辑。所以下面对拦截器的具体逻辑再总结一下:

  1. 在发起请求前对request进行处理。
  2. 调用下一个拦截器,获取response。
  3. 对response进行处理,返回给上一个拦截器。
4)拦截器的作用

来自官网的英文原文:

    Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
意思大概是,拦截器是一个强有力的机制,能够监控,重写以及重试(请求的)调用。

以下是官网提供的一个Interceptor例子,只用来打印拦截到的Response的相关日志:

public class LoggingInterceptor implements Interceptor {

    String TAG = "LoggingInterceptor";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        long t1 = System.nanoTime();
        Log.d(TAG, String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));
        Response response = chain.proceed(request);

        long t2 = System.nanoTime();
        Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (t2 - t1) / 1e6d, response.headers()));

        return response;
    }
}

只是一个 Interceptor 太抽象,下面我通过一个具体的Activity展示拦截器的用法:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MAINTAG";
    private static final String URL = "https://www.baidu.com";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testAsyncGetRequest();
    }

    //这是一个普通的okhttp异步get请求
    private void testAsyncGetRequest() {
        OkHttpClient okHttpClient = new OkHttpClient().newBuilder().addInterceptor(new LoggingInterceptor()).build();//新建一个okhttpClient对象,并且设置拦截器
        Request request = new Request.Builder().url(URL).build();//新建Request对象
        Callback callback = new Callback() {// 新建异步请求的回调函数
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "request Failed ; " + e.getLocalizedMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    Log.d(TAG, "onResponse:" + response.body().string());
                } else {
                    Log.d(TAG, "onResponse failed");
                }
            }
        };
        okHttpClient.newCall(request).enqueue(callback);//用okhttpClient执行request,并且注册回调函数

    }

    /**
     * 这是按照官方的示例Interceptor改的,打印日志的方式换成了Log.d().
     */
    class LoggingInterceptor implements Interceptor {

        @Override
        public Response intercept(Interceptor.Chain chain) throws IOException {
            //第一步,获得chain内的request
            Request request = chain.request();
            Log.d(TAG, "intercept-request:" + request.url());
            //第二步,用chain执行request
            Response response = chain.proceed(request);
            Log.d(TAG, "intercept-response" + "-" + response.request().url());
            //第三步,返回response
            return response;
        }
    }
}

对照最上方的 Okhttp 工作流程图,可以观察到,在 OkhttpCore 即将把 response 返回给 application 时,拦截器率先得到了 response 对象。而在上方的代码中,只是对 request 和 response 进行了日志打印,并没有实际操作。

但是事实上,拦截器拿到了 request 之后,可以对 request 进行重写,可以添加,移除,替换请求头,也能对 response 的 header 进行重写,改变 response 的 body。佐证如下,文档英文原文:

Rewriting Requests

Interceptors can add, remove, or replace request headers. They can also transform the body of those requests that have one. For example, you can use an application interceptor to add request body compression if you’re connecting to a webserver known to support it.

Rewriting Responses

Symmetrically, interceptors can rewrite response headers and transform the response body. This is generally more dangerous than rewriting request headers because it may violate the webserver’s expectations!

上面的 Activity 运行起来之后,可以在日志中看到如下内容:证明,拦截器确实可以获得 response。

2022-10-11 17:06:58.443 13622-13658/com.lwm.okhttpinterceptorapp D/MAINTAG: intercept-request:https://www.baidu.com/
2022-10-11 17:06:58.733 13622-13658/com.lwm.okhttpinterceptorapp D/MAINTAG: intercept-response-https://www.baidu.com/
2022-10-11 17:06:58.738 13622-13658/com.lwm.okhttpinterceptorapp D/MAINTAG: onResponse:<!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title>

综上所述,拦截器的作用就是:可以在应用拿到 response 之前,先获得 response,对其中某些数据进行监控,在有必要的情况下,对 response 的某些内容(比如 response 的 header,body,response 内的 request 的 header,body)进行更改。

参考:
    okhttp初识拦截器

    Okhttp之 拦截器(Interceptor)专题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值