OkHttp拦截器的实现原理
今天项目中遇到需要将从push接收到的数据按照协议parse成应用层需要的结构化数据类型问题;因为push消息类型繁多,等待解析出的结构化数据类型也多样,有的还需要经过几步的parse过程;而且因为项目历史原因,使用Protocal Buffer(push的数据是字节形式的传递)。中间尝试了代理、装饰等模式,都失败了,最终还是使用了原始的继承解决问题;在解决问题个过程中也学习了下OkHttp中的拦截器实现,这里做简单记录:
-
拦截器的接口定义如下:
可以看到,在拦截器的回调中,我们可以拿到两个重要的参数Request和Response,而接口在回调时,会接收一个Chain类型的参数,这个参数保存了Request和Response的相关数据。
-
看一个简单的Interceptor接口实现例子:
该拦截器实例的功能只是在请求发出前和接收到响应后,分别打印log。Response response=chain.proceed(request); 很重要,它是将拦截连串起来的关键。
- 再看下,一个网络请求的应用吧:
跟进,OkHttpClient.Builder的addInterceptor()方法,可以看到,在OkHttp内部是使用了List保存了添加的所有拦截器。
- 跟进client.newCall(request).execute();的执行过程,查看拦截器的串联过程:
从上面的方法中可以看到,请求默认会被构造成RealCall类型,再进一步查看RealCall的execute()方法:
上面的红色方框中这行代码,它并没有真正的执行网络请求,而只是简单地将请求放入请求池中,让它等待分派器的后续执行。而真正的执行体在上面的蓝色方框中,它封装了一个拦截器链(Chain),并调用了Chain的proced方法,传入原始的request对象,这里开始拦截器链的调用过程:
- 查看proceed方法,可以看到,它使用循环+递归的方式,借助函数调用栈,将拦截器串联起来:
上面的代码中,真正执行网络请求的是最后一行绿色方框中的代码,而当存在多个拦截器时,每个拦截器在执行时都会在上图的蓝色方框中的代码地方阻塞,等待下一个拦截器的调用返回。
- 为了说明清楚,下面分别以 拦截器链中有1个、2个拦截器的场景加以模拟: