前言
在前两篇中主要讲解了OkHttp源码解析,在本篇中,将会结合前两篇所有的知识点,从零开始手写一份阉割版的OkHttp框架。因此,读者也可以按照本章的方式从零开始一步一步手仿造出OkHttp框架。
在开始之前,我们先整理一下,需要按照什么样的步骤才能仿造一个阉割版的OkHttp。
- 依葫芦画瓢,先创造身体,复制一份,再注入灵魂
- 创造Request 对象,再造 Response 对象
- 流程图:分发器、责任链、拦截器
- 分发器:执行队列、等待队列、线程池、逻辑判断、线程结束
- 拦截器:对应拦截器的职责干什么 (专一,只做自己的事)
- 责任链模式:肯定有一个chain接口和其他实现类,遵循对类隐藏,对接口暴露
- 辅助类完成
1、先创造对应雏形
我们先看原本OkHttp使用是怎样的
Request request = new Request.Builder().url(PATH).build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) {
content = response.body().toString();
mHandler.sendEmptyMessage(1);
}
});
Ok,原本OkHttp使用是这样的,我们先复制一份,然后在每一个变量都加上属于自己的后缀,表示是我们自己的,于是就成了。
Request2 request2 = new Request2.Builder().url(PATH).build();
OkHttpClient2 okHttpClient2 = new OkHttpClient2.Builder().build();
Call2 call2 = okHttpClient2.newCall(request2);
call2.enqueue(new Callback2() {
@Override
public void onFailure(Call2 call, IOException e) {
}
@Override
public void onResponse(Call2 call, Response2 response) {
content = response.body().toString();
mHandler.sendEmptyMessage(1);
}
});
现在肯定代码全报红,因为没有对应的类,既然没有那我们一个一个的创造,代码报红也一个个解决。先按从上到下的顺序解决,创建一个全新的类 Request2 ,先不写任何逻辑,先把报红的问题解决了,于是乎对应的Request2 为: Request2.class
public class Request2 {
public static class Builder {
public Builder() {
}
public Builder url(String url) {
return this;
}
public Request2 build() {
return new Request2();
}
}
}
这个不用多说了吧,典型的建造者模型。接着我们分析 OkHttpClient2 类和 Request2 使用方式几乎一样,只不过少了 url方法,于是乎 OkHttpClient2 雏形也出来了。
OkHttpClient2.class
public class OkHttpClient2 {
public Call2 newCall(Request2 request2) {
return new RealCall2();
}
public static class Builder {
public Builder() {
}
public OkHttpClient2 build() {
return new OkHttpClient2();
}
}
}
接下来就是待实现的Call2 ,以及 Call2 对应的实现类 RealCall2。
Call2.class
public interface Call2 {
void enqueue(Callback2 callback2);
}
RealCall2.class
public class RealCall2 implements Call2{
@Override
public void enqueue(Callback2 callback2) {
}
}
现在 Callback2 没有,继续创建
Callback2.class
public interface Callback2 {
public void onFailure(Call2 call, IOException e);
public void onResponse(Call2 call, Response2 response);
}
现在 只有 Response2 相关的报错了,继续创建
Response2.class
public class Response2 {
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public ResponseBody body() {
return body;
}
public void setBody(ResponseBody body) {
this.body = body;
}
private int statusCode;
private ResponseBody body;
}
这里就需要创建响应体 ResponseBody 。
ResponseBody.class
public class ResponseBody {
private InputStream inputStream;
private String bodyString;
private long contentLength;
private byte[] bytes;
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
public String string() {
return bodyString;
}
public ResponseBody setBodyString(String bodyString) {
this.bodyString = bodyString;
return this;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public byte[] getBytes() {
return bytes;
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
}
到这里,手写OkHttp的外壳已经写好了,代码也没有报红了。接下来该慢慢注入灵魂了。
2、向 Request2.class 注入灵魂
这里我们还是按照从上到下的顺序依次注入灵魂。先Request2 ,顾名思义,这个类主要封装的请求相关的信息,既然是请求相关的信息,我们先整理下,请求接口时,需要哪些东西?
- 请求方式 String requestMethod
- 请求地址 String url
- 请求头 Map<String, String> mHeaderList
- 请求体 RequestBody2 requestBody2
- 重定向地址 String redictUrl
- 是否缓存等等
这里 RequestBody2 是没有的,那么。
RequestBody2.class
public class RequestBody2 {
public static final String TYPE = "application/x-www-form-urlencoded";
private final String ENC = "utf-8";
Map<String, String> bodys = new HashMap<>();
public void addBody(String key, String value) {
try {
bodys.put(URLEncoder.encode(key, ENC), URLEncoder.encode(value, ENC));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String getBody() {
StringBuffer stringBuffer = new StringBuffer();
for (Map.Entry<String, String> stringStringEntry : bodys.entrySet()) {
stringBuffer.append(stringStringEntry.getKey())
.append("=")
.append(stringStringEntry.getValue())
.append("&");
}
if (stringBuffer.length() != 0 ) {
stringBuffer.deleteCharAt(stringBuffer.length() -1);
}
return stringBuffer.toString();
}
}
这没啥可说的,就一个很简单的添加获取的方式,那么注入灵魂后的 Request2 为:
Request2.class
public class Request2 {
public static final String GET = "GET";
public static final String POST = "POST";
private String url;
private String requestMethod = GET;
private Map<String, String> mHeaderList = new HashMap<>();
private RequestBody2 requestBody2;
private Builder builder;
private boolean isCache;
private String redictUrl;
public boolean isCache() {
return isCache;
}
public void setCache(boolean cache) {
isCache = cache;
}
public String getRedictUrl() {
return redictUrl;
}
public void setRedictUrl(String redictUrl) {
this.redictUrl = redictUrl;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getRequestMethod() {
return requestMethod;
}
public Map<String, String> getmHeaderList() {
return mHeaderList;
}
public RequestBody2 getRequestBody2() {
return requestBody2;
}
public Request2() {
this(new Builder());
}
public Builder builder(){
return builder;
}
public Request2(Builder builder) {
this.builder = builder;
this.url = builder.url;
this.requestMethod = builder.requestMethod;
this.mHeaderList = builder.mHeaderList;
this.requestBody2 = builder.requestBody2;
}
public static class Builder {
private String url;
private String requestMethod = GET;
private Map<String, String> mHeaderList = new HashMap<>();
private RequestBody2 requestBody2;
public Builder url(String url) {
this.url = url;
return this;
}
public Builder get() {
requestMethod = GET;
return this;
}
public Builder post(RequestBody2 requestBody2) {
requestMethod = POST;
this.requestBody2 = requestBody2;
return this;
}
public Builder addRequestHeader(String key, String value) {
mHeaderList.put(key, value);
return this;
}
public Builder removeRequestHeader(String key) {
if(mHeaderList!=null) {
mHeaderList.remove(key);
}
return this;
}
public Request2 build() {
return new Request2(this);
}
}
}
到这里请求体Request2.class差不多结束了,没啥难度,接下来轮到 OkHttpClient2 了
3、给OkHttpClient2.class注入灵魂
在开始之前,先上流程图,看看OkHttpClient2 到底做了些什么事。 如图所示 OkHttpClient2 通过 newCall方法 返回对应Call的实现类对象 RealCall,然后再通过 RealCall 分别调用 Dispatcher对象的 execute 和 enqueue方法,接着依次调用对应的拦截器,最终产生 Response对象。在上一篇讲解拦截器的时候,除了系统的五大拦截器,开发者可以自定义拦截器,这里再贴一下源码。
Response getResponseWithInterceptorChain() throws IOException {
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 (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
originalRequest, this, eventListener, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
return chain.proceed(originalRequest);
}
从源码上看,开发者自定义的拦截器 是通过 OkHttpClient 对象添加的,也就是说,我们手写的对应 OkHttpClient2 也要有开发者可自定义拦截器的功能。
OkHttpClient2.class
public class OkHttpClient2 {
int recount;
Dispatcher2 dispatcher;
List<Interceptor2> myInterceptors=new ArrayList<>();
public OkHttpClient2(Builder builder) {
this.recount = builder.recount;
this.dispatcher = builder.dispatcher;
this.myInterceptors = builder.myInterceptors;
}
public Dispatcher2 dispatcher(){
return dispatcher;
}
public int getRecount() {
return recount;
}
public Call2 newCall(Request2 request){
return new RealCall2(this,request);
}
public static class Builder{
List<Interceptor2> myInterceptors=new ArrayList<>();
int recount = 3;
Dispatcher2 dispatcher;
public Builder(){
dispatcher = new Dispatcher2();
}
public Builder addInterceptor(Interceptor2 interceptor2){
myInterceptors.add(interceptor2);
return this;
}
public void setRecount(int recount) {
this.recount = recount;
}
public OkHttpClient2 build(){
return new OkHttpClient2(this);
}
}
public List<Interceptor2> getMyInterceptors() {
return myInterceptors;
}
}
这里创建了全新的对象 Interceptor2拦截器接口、Dispatcher2 分发器,根据上面的流程图可知,分发器中有同步和异步方法,这里就只实现异步方法
Dispatcher2.class
public class Dispatcher2 {
public void enqueue(RealCall2.AsyncCall2 call){
}
}
最新的RealCall2.class
public class RealCall2 implements Call2{
public RealCall2(OkHttpClient2 okHttpClient2, Request2 request) {
}
@Override
public void enqueue(Callback2 callback2) {
}
class AsyncCall2 implements Runnable{
@Override
public void run() {
}
}
}
Interceptor2.class
public interface Interceptor2 {
Response2 doNext(Chain2 chain2);
}
因为拦截器是多个,并且每一个功能点都不同,然后每一个拦截器都与下一个相互关联,所以这里又扯出了责任链,在上一篇文章我们可知,所有责任链拦截器的功能就是将 Request 转 变成 getResponse,所以先定义一个实现类 Chain2。
Chain2.class
public interface Chain2 {
Request2 getRequest();
Response2 getResponse(Request2 request2);
}
Chain2实现类ChainManager.class
public class ChainManager implements Chain2{
@Override
public Request2 getRequest() {
return null;
}
@Override
public Response2 getResponse(Request2 request2) {
return null;
}
}
到这OkHttpClient2.class相关的灵魂也注入完毕,接下来该写高并发分发器了。
4、高并发分发器实现
在关于OkHttp第一篇中,主要讲解了分发器,以及双管队列是如何实现的,这里不多叙述,直接上图。 所图所示
在Dispatcher中,里面是有俩个队列,于是乎。
public class Dispatcher2 {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Deque<RealCall2.AsyncCall2> runningAsyncCalls = new ArrayDeque<>();
private Deque<RealCall2.AsyncCall2> readyAsyncCalls = new ArrayDeque<>();
public void enqueue(RealCall2.AsyncCall2 call){
if(runningAsyncCalls.size()<maxRequests && runningCallsForHost(call)<maxRequestsPerHost){
runningAsyncCalls.add(call);
executorService().execute(call);
}else{
readyAsyncCalls.add(call);
}
}
private int runningCallsForHost(RealCall2.AsyncCall2 call) {
int count = 0;
if(runningAsyncCalls.isEmpty()){
return 0;
}
SocketRequestServer srs = new SocketRequestServer();
for(RealCall2.AsyncCall2 runningAsyncCall:runningAsyncCalls){
if(srs.getHost(runningAsyncCall.getRequest()).equals(call.getRequest())){
count++;
}
}
return count;
}
public ExecutorService executorService(){
ExecutorService service = ThreadPoolManager.getInstance().getExecutor();
return service;
}
public void finished(RealCall2.AsyncCall2 call2){
runningAsyncCalls.remove(call2);
if(readyAsyncCalls.isEmpty()){
return;
}
for(RealCall2.AsyncCall2 readyAsyncCall:readyAsyncCalls){
readyAsyncCalls.remove(readyAsyncCall);
runningAsyncCalls.add(readyAsyncCall);
executorService().execute(readyAsyncCall);
}
}
}
这里用了两个辅助类,里面没啥逻辑而言,直接贴出来
SocketRequestServer.class
public class SocketRequestServer {
private final String K = " ";
private final String VIERSION = "HTTP/1.1";
private final String GRGN = "\r\n";
public String getHost(Request2 request2) {
try {
URL url = new URL(request2.getUrl());
return url.getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
}
public int getPort(Request2 request2) {
try {
URL url = new URL(request2.getUrl());
int port = url.getPort();
return port == -1 ? url.getDefaultPort() : port;
} catch (MalformedURLException e) {
e.printStackTrace();
}
return -1;
}
public String getRequestHeaderAll(Request2 request2) {
URL url = null;
try {
url = new URL(request2.getUrl());
} catch (MalformedURLException e) {
e.printStackTrace();
}
String file = url.getFile();
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(request2.getRequestMethod())
.append(K)
.append(file)
.append(K)
.append(VIERSION)
.append(GRGN);
if (!request2.getmHeaderList().isEmpty()) {
Map<String,String> mapList = request2.getmHeaderList();
for (Map.Entry<String, String> entry: mapList.entrySet()) {
stringBuffer.append(entry.getKey())
.append(":").append(K)
.append(entry.getValue())
.append(GRGN);
}
stringBuffer.append(GRGN);
}
if ("POST".equalsIgnoreCase(request2.getRequestMethod())) {
stringBuffer.append(request2.getRequestBody2().getBody()).append(GRGN);
}
return stringBuffer.toString();
}
}
这里就是请求体的拼接,很简单一看就能明白的那种。
ThreadPoolManager.class
public class ThreadPoolManager {
private static ThreadPoolManager mInstance = new ThreadPoolManager();
public static ThreadPoolManager getInstance() {
return mInstance;
}
private int corePoolSize;
private int maximumPoolSize;
private long keepAliveTime = 1;
private TimeUnit unit = TimeUnit.HOURS;
private ThreadPoolExecutor executor;
private ThreadPoolManager() {
corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1;
maximumPoolSize = corePoolSize;
executor = new ThreadPoolExecutor(
0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
public ThreadPoolExecutor getExecutor() {
return executor;
}
public void execute(Runnable runnable) {
if (runnable == null) return;
executor.execute(runnable);
}
public void remove(Runnable runnable) {
if (runnable == null) return;
executor.remove(runnable);
}
}
这个类,就是线程池管理类。
最新的RealCall2.class
public class RealCall2 implements Call2{
private OkHttpClient2 okHttpClient2;
private Request2 request2;
public OkHttpClient2 getOkHttpClient2(){
return okHttpClient2;
}
public RealCall2(OkHttpClient2 okHttpClient2, Request2 request2) {
this.okHttpClient2 = okHttpClient2;
this.request2 = request2;
}
@Override
public void enqueue(Callback2 callback2) {
okHttpClient2.dispatcher().enqueue(new AsyncCall2(callback2));
}
class AsyncCall2 implements Runnable{
private Callback2 callback2;
public AsyncCall2(Callback2 callback) {
this.callback2 = callback;
}
Request2 getRequest(){
return RealCall2.this.request2;
}
@Override
public void run() {
}
}
}
到这里分发器已经写完了,现在该是责任链了。
5、责任链实现
上面我们通过 ChainManager 实现了 Chain2 接口,但并没有做任何处理,现在就轮到给责任链注入灵魂了。
**最新的ChainManager **
public class ChainManager implements Chain2 {
private int index;
private Call2 call;
private Request2 request2;
private List<Interceptor2> interceptors;
public Call2 getCall(){
return call;
}
public ChainManager(int index, Call2 call, Request2 request2, List<Interceptor2> interceptors) {
this.index = index;
this.call = call;
this.request2 = request2;
this.interceptors = interceptors;
}
@Override
public Request2 getRequest() {
return request2;
}
@Override
public Response2 getResponse(Request2 request2) {
if(interceptors==null || interceptors.isEmpty()){
return new Response2();
}
if(index>=interceptors.size()){
return new Response2();
}
Interceptor2 interceptor2 = interceptors.get(index);
ChainManager manager = new ChainManager(index+1,call,request2,interceptors);
Response2 response2 = interceptor2.doNext(manager);
return response2;
}
}
所谓的责任链,你可以把它看成类似于铁链的一种东西,上一环扣着下一环,上一环的终点表示下一环 的起点。责任链实现就这么简单。接下来就轮到 拦截器的实现了。
6、拦截器的实现
在OkHttp第二篇中,已经讲过,每个拦截器的作用,在这也将会一个个从零手写出对应的拦截器。 我这准备了
- RetryAndFollowInterceptor 重定向拦截器
- BridgeInterceptor 桥接拦截器
- CacheInterceptor 缓存拦截器
- CallServerInteceptor 连接+读写拦截器
现在准备依次实现
6.1 实现重定向拦截器
RetryAndFollowInterceptor.class
public class RetryAndFollowInterceptor implements Interceptor2 {
@Override
public Response2 doNext(Chain2 chain2){
System.out.println("我是重试重定向拦截器,执行了");
ChainManager chainManager = (ChainManager) chain2;
RealCall2 realCall2 = (RealCall2) chainManager.getCall();
OkHttpClient2 client2 = realCall2.getOkHttpClient2();
IOException ioException = null;
if (client2.getRecount() != 0) {
for (int i = 0; i < client2.getRecount(); i++) {
try {
System.out.println("我是重试拦截器,我要Return Response2了");
Response2 response2 = chain2.getResponse(chainManager.getRequest());
Request2 request2 = chainManager.getRequest();
if(!TextUtils.isEmpty(request2.getRedictUrl())){
request2.setUrl(request2.getRedictUrl());
return chain2.getResponse(request2);
}
return response2;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return new Response2();
}
}
这里就只处理了重定向的逻辑,只要在重试范围内,就能重定向。
6.2 实现桥接拦截器
BridgeInterceptor.class
public class BridgeInterceptor implements Interceptor2 {
@Override
public Response2 doNext(Chain2 chain2){
ChainManager chainManager = (ChainManager) chain2;
Request2 request2 = chain2.getRequest();
Map<String,String> mHeadList = request2.getmHeaderList();
mHeadList.put("Host",new SocketRequestServer().getHost(chainManager.getRequest()));
if("POST".equalsIgnoreCase(request2.getRequestMethod())){
mHeadList.put("Content-Length", request2.getRequestBody2().getBody().length()+"");
mHeadList.put("Content-Type", RequestBody2.TYPE);
}
return chain2.getResponse(request2);
}
}
这里也只做了 很简单的 请求头的封装处理
6.3 实现缓存拦截器
CacheInterceptor.class
public class CacheInterceptor implements Interceptor2 {
@Override
public Response2 doNext(Chain2 chain){
Request2 request = chain.getRequest();
request = request.builder()
.removeRequestHeader("pragma")
.addRequestHeader("Cache-Control", "max-age=60")
.build();
String json = CacheTemp.cacheMap.get(request.getUrl());
if(!TextUtils.isEmpty(json)){
Response2 response2 = new Response2();
ResponseBody body = new ResponseBody();
body.setBodyString(json);
response2.setBody(body);
return response2;
}
if(request.getRequestMethod().equals("GET")) {
CacheTemp.isCache = false;
}else{
CacheTemp.isCache = false;
}
return chain.getResponse(request);
}
}
这里用到了缓存辅助类 CacheTemp
CacheTemp.class
public class CacheTemp {
public static boolean isCache;
public static Map<String,String> cacheMap=new HashMap<>();
}
我这里就没有像OkHttp那样写了个SD卡文件流缓存。毕竟阉割版,当然读者这里可以吧 磁盘缓存,软缓存都用上。
6.4 连接+读写拦截器
CallServerInteceptor.class
public class CallServerInteceptor implements Interceptor2 {
@Override
public Response2 doNext(Chain2 chain2){
Response2 response2 = new Response2();
try {
SocketRequestServer srs = new SocketRequestServer();
Request2 request2 = chain2.getRequest();
Socket socket = new Socket(srs.getHost(request2), srs.getPort(request2));
OutputStream os = socket.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(os));
String requestAll = srs.getRequestHeaderAll(request2);
System.out.println("requestAll:" + requestAll);
bufferedWriter.write(requestAll);
bufferedWriter.flush();
InputStream is = socket.getInputStream();
HttpCodec httpCodec = new HttpCodec();
String responseLine = httpCodec.readLine(is);
System.out.println("响应行:" + responseLine);
Map<String, String> headers = httpCodec.readHeaders(is);
for (Map.Entry<String, String> entry : headers.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
if (headers.containsKey("Location")) {
request2.setRedictUrl(headers.get("Location"));
}
if (headers.containsKey("Content-Length")) {
int length = Integer.valueOf(headers.get("Content-Length"));
byte[] bytes = httpCodec.readBytes(is, length);
String content = new String(bytes);
System.out.println("响应:" + content);
if (request2.isCache()) {
CacheTemp.cacheMap.put(request2.getUrl(), content);
}
ResponseBody responseBody = new ResponseBody();
responseBody.setContentLength(length);
responseBody.setBytes(bytes);
response2.setBody(responseBody);
} else {
String response = httpCodec.readChunked(is);
if (CacheTemp.isCache) {
CacheTemp.cacheMap.put(request2.getUrl(), response);
}
response2.setBody(new ResponseBody().setBodyString(response.replaceAll("\\r\\n", "")));
System.out.println("响应:" + response);
}
is.close();
socket.close();
}catch (Exception e){
e.printStackTrace();
}
return response2;
}
}
我这里就没有完全像OkHttp那样,这里就偷点懒吧连接拦截器和读写拦截器写在一起了。到这里所有的拦截器实现已经全部写完了。接下来就开始组装拦截器了。
7、使用拦截器
依然用这张流程图,我们看到使用拦截器是在Dispatcher后才使用的,而真正同步异步是在RealCall触发的,也就是说,在RealCall就要把拦截器组装好。那么最终的RealCall代码。
RealCall2.class
public class RealCall2 implements Call2{
private OkHttpClient2 okHttpClient2;
private Request2 request2;
public OkHttpClient2 getOkHttpClient2(){
return okHttpClient2;
}
public RealCall2(OkHttpClient2 okHttpClient2, Request2 request2) {
this.okHttpClient2 = okHttpClient2;
this.request2 = request2;
}
@Override
public void enqueue(Callback2 callback2) {
okHttpClient2.dispatcher().enqueue(new AsyncCall2(callback2));
}
class AsyncCall2 implements Runnable{
private Callback2 callback2;
public AsyncCall2(Callback2 callback) {
this.callback2 = callback;
}
Request2 getRequest(){
return RealCall2.this.request2;
}
@Override
public void run() {
try {
Response2 response2 = getResponseChain();
callback2.onResponse(RealCall2.this, response2);
}catch (Exception e){
callback2.onFailure(RealCall2.this, new IOException("OKHTTP getResponseWithInterceptorChain 错误... e:" + e.toString()));
}finally {
okHttpClient2.dispatcher().finished(this);
}
}
private Response2 getResponseChain() {
List<Interceptor2> interceptor2s = new ArrayList<>();
interceptor2s.add(new BridgeInterceptor());
interceptor2s.add(new RetryAndFollowInterceptor());
interceptor2s.add(new CacheInterceptor());
interceptor2s.addAll(okHttpClient2.getMyInterceptors());
interceptor2s.add(new CallServerInteceptor());
ChainManager chainManager = new ChainManager(0,RealCall2.this,request2,interceptor2s);
return chainManager.getResponse(request2);
}
}
}
到这里,手写OkHttp框架快要接近尾声了,我们先跑一遍,看下效果如何。
看到这结果,还是翻车了哇,我们来分析一波,日志里面报的是302,响应头 Location 里面也有对应的属性,这无非就是典型的被重定向了。但是仔细看了下,却发现重定向的地址,和请求的地址完全一样。这就有点奇怪了,将这个地址拿到浏览器试试。 在浏览器里面发现这根本就没有重定向操作。所以根本原因根本就不是因为重定向而导致的问题。 那会不会是没有适配Https的网络请求? 我们都知道Http和Https之间是有一层SSL证书,而我们连接拦截器里面根本就没有响应的处理,而是直接连接Socket。于是乎
Socket socket = null;
if(request2.getUrl().startsWith("https://")){
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
sslContext.init(null, trustManagers, null);
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(srs.getHost(request2), srs.getPort(request2)));
}else {
socket = new Socket(srs.getHost(request2), srs.getPort(request2));
}
我在连接拦截器里面加了 这个判断,如果请求为https的话,将会自动加上证书认证,http请求的话,就直接初始化socket。接着在运行试试。
到这里,一个阉割版的OkHttp框架结束了。相信看到这里的小伙伴,应该完全熟悉OkHttp是怎么走的了。
8、Demo地址 点我下载
|