OKhttp拦截器实现实践环节源码解析
itbird01 人气:0正文
本节我们开始自我实现我们自己okhttp框架中的每个拦截器。
先简单回顾一下各个拦截器的作用:
- RetryAndFollowUpInterceptor:重试拦截器
处理重试的一个拦截器,会去处理一些异常,根据底层返回的响应数据,先进行一些特殊状态码的判断,例如:如果底层返回307,则根据服务端返回的最新location,重新构建新的请求,交由底层拦截器,重新发起请求。如果底层返回路由异常、某些IO异常,则会continue,重新发起请求。
- BridgeInterceptor:基础的拦截器
给我们平常发起的请求,添加通用和请求首部信息,做一个简单的处理,设置一些通用的请求头,Cookie、Connection、Content-Type、Content-Length,做一些返回的处理,如果返回的数据被压缩了,采用 ZipSource,保存Cookie。
- CacheInterceptor:缓存拦截器
缓存存储策略、缓存过期策略、缓存对比策略的具体实现。
- ConnectInterceptor:连接的拦截器
ConnectInterceptor负责连接复用、建立socket连接,okio与socket输入输出流绑定。
- CallServerInterceptor: 具体与服务器通信,给服务器写数据和读取数据;
拦截器的自我实现
好了,接下来,把我们之前写的框架的代码,重新梳理一下,新增一下几个拦截器。 RealCall.java
package com.itbird.okhttpstudy.okhttp; import android.util.Log; import com.itbird.okhttpstudy.interceptor.BridgeInterceptor; import com.itbird.okhttpstudy.interceptor.CacheInterceptor; import com.itbird.okhttpstudy.interceptor.CallServerInterceptor; import com.itbird.okhttpstudy.interceptor.ConnectInterceptor; import com.itbird.okhttpstudy.interceptor.Interceptor; import com.itbird.okhttpstudy.interceptor.RetryAndFollowUpInterceptor; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Created by itbird on 2022/11/21 */ public class RealCall implements Call { private Request request; private OkhttpClient okhttpClient; public RealCall(Request request, OkhttpClient okhttpClient) { this.request = request; this.okhttpClient = okhttpClient; } @Override public void enqueue(Callback callback) { okhttpClient.dispatcher().enqueue(new AsyncCall(callback)); } @Override public Response execute() { return getResponseWithInterceptorChain(); } @Override public Request request() { return request; } private Response getResponseWithInterceptorChain() { List<Interceptor> interceptors = new ArrayList<Interceptor>(); interceptors.add(new BridgeInterceptor());// 基础 interceptors.add(new CacheInterceptor());// 缓存 interceptors.add(new ConnectInterceptor());// 建立连接 interceptors.add(new CallServerInterceptor());// 写数据 interceptors.add(new RetryAndFollowUpInterceptor());// 重试 Interceptor.Chain chain = new RealInterceptorChain(this, interceptors, request); try { return chain.proceed(request); } catch (IOException e) { //处理过程被中断时,通过错误码返回 return null; } } class AsyncCall extends NamedRunnable { private Callback callback; public AsyncCall(Callback callback) { this.callback = callback; } @Override public void execute() { Log.d(Constants.TAG, "AsyncCall execute"); //这里有问题的 Response response = getResponseWithInterceptorChain(); if (callback != null) { try { callback.onResponse(RealCall.this, response); } catch (IOException e) { } } } } }
接下来还是老办法,按照AS提示,新建这些类。
RetryAndFollowUpInterceptor
package com.itbird.okhttpstudy.interceptor; import android.util.Log; import com.itbird.okhttpstudy.okhttp.Constants; import com.itbird.okhttpstudy.okhttp.Request; import com.itbird.okhttpstudy.okhttp.Response; import java.io.IOException; /** * Created by itbird on 2022/11/24 */ public class RetryAndFollowUpInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Log.d(Constants.TAG, "RetryAndFollowUpInterceptor"); Request request = chain.request(); //okhttp表现为,此处,去根据底层抛出的异常,决定是否为关键错误异常,如果不是,则while true循环,去执行重试请求 return chain.proceed(request); } }
BridgeInterceptor
package com.itbird.okhttpstudy.interceptor; import android.util.Log; import com.itbird.okhttpstudy.okhttp.Constants; import com.itbird.okhttpstudy.okhttp.Request; import com.itbird.okhttpstudy.okhttp.RequsetBody; import com.itbird.okhttpstudy.okhttp.Response; import java.io.IOException; /** * Created by itbird on 2022/11/24 */ public class BridgeInterceptor implements Interceptor { public BridgeInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d(Constants.TAG, "BridgeInterceptor"); Request request = chain.request(); // 添加一些请求头 // request.addParam("Connection", "keep-alive"); // 做一些其他处理 if (request.requsetBody() != null) { RequsetBody requestBody = request.requsetBody(); request.addParam("Content-Type", requestBody.getContentType()); request.addParam("Content-Length", Long.toString(requestBody.getContentLength())); } //GZIP数据流转换 return chain.proceed(request); } }
CacheInterceptor
package com.itbird.okhttpstudy.interceptor; import android.util.Log; import com.itbird.okhttpstudy.okhttp.CacheControl; import com.itbird.okhttpstudy.okhttp.Constants; import com.itbird.okhttpstudy.okhttp.Request; import com.itbird.okhttpstudy.okhttp.Response; import java.io.IOException; /** * Created by itbird on 2022/11/24 */ public class CacheInterceptor implements Interceptor { public CacheInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d(Constants.TAG, "CacheInterceptor"); Request request = chain.request(); if (request.cache() == CacheControl.FORCE_CACHE) { //本地缓存有没有,缓存过期了没有,缓存对比服务器返回307 } return chain.proceed(request); } }
ConnectInterceptor
package com.itbird.okhttpstudy.interceptor; import android.util.Log; import com.itbird.okhttpstudy.okhttp.Constants; import com.itbird.okhttpstudy.okhttp.Request; import com.itbird.okhttpstudy.okhttp.Response; import java.io.IOException; /** * Created by itbird on 2022/11/24 */ public class ConnectInterceptor implements Interceptor { public ConnectInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d(Constants.TAG, "ConnectInterceptor"); Request request = chain.request(); //表现为okhttp的话,这里就是socket简历连接,并且将socket输入输出流与okio绑定在一起 return chain.proceed(request); } }
CallServerInterceptor
package com.itbird.okhttpstudy.interceptor; import android.util.Log; import com.itbird.okhttpstudy.okhttp.Constants; import com.itbird.okhttpstudy.okhttp.Request; import com.itbird.okhttpstudy.okhttp.Response; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by itbird on 2022/11/24 */ public class CallServerInterceptor implements Interceptor { public CallServerInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d(Constants.TAG, "CallServerInterceptor"); Request request = chain.request(); try { //获取连接请求 URL url = new URL(request.url()); HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); //设置连接超时 httpURLConnection.setConnectTimeout(3000); //设置方法 httpURLConnection.setRequestMethod(request.method()); if (request.requsetBody() != null) { httpURLConnection.setRequestProperty("Content-Type", request.requsetBody().getContentType()); httpURLConnection.setRequestProperty("Content-Length", String.valueOf(request.requsetBody().getContentLength())); Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Length")); Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Type")); } //开始连接 httpURLConnection.connect(); //插入,如果requsetbody不为空,则继续写入内容 if (request.requsetBody() != null) { request.requsetBody().writeBodyData(httpURLConnection.getOutputStream()); } //判断返回的状态码 if (httpURLConnection.getResponseCode() == 200) { //获取返回的数据 InputStream inputStream = httpURLConnection.getInputStream(); //将返回的数据,封装为response Response response = new Response(inputStream); return response; } } catch (MalformedURLException e) { throw e; } catch (IOException e) { throw e; } return null; } }
运行一下
题外话
说到责任链模式,这里有一个题外话,我们之前分析view事件源码的时候,也看到过,view 事件源码,也是责任链机制,它是通过每层返回true、false来决定是否拦截。
大家想一下,和okhttp这里的责任链有啥不同的?我们上面查看okhttp源码的时候知道,它并不是通过每层返回true or false来决定是否拦截的,而是根据每层返回的response 以及 是否抛出异常来决定是否拦截。
加载全部内容