博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Okhttp的源码解读
阅读量:4294 次
发布时间:2019-05-27

本文共 10053 字,大约阅读时间需要 33 分钟。

重要的类

类名 描述
OkHttpClient OkHttp请求客户端,Builder模式实现
Dispatcher 本质是异步请求的调度器,负责调度异步请求的执行,控制最大请求并发数和单个主机的最大并发数,并持有有一个线程池负责执行异步请求,对同步请求只是作统计操作。
Request 封装网络请求,就是构建请求参数(如url,header,请求方式,请求参数),Builder模式实现
Response 网络请求对应的响应,Builder模式实现,真正的Response是通过RealCall.getResponseWithInterceptorChain()方法获取的。
Call 是根据Request生成的一个具体的请求实例,且一个Call只能被执行一次。
ConnectionPool 连接池
Interceptor Interceptor可以说是OkHttp的核心功能,它就是通过Interceptor来完成监控管理,重写和重试请求的。
Cache 可以自定义是否采用缓存,缓存形式是磁盘缓存,DiskLruCache。

源码思想

OkHttp3,网络请求库,同步请求RealCall.execute()和异步请求RealCall.enqueue(),请求任务都是交给Dispatcher调度请求任务的处理,请求通过一条拦截链,每一个拦截器处理一部分工作,最后一个拦截器,完成获取请求任务的响应,会将响应沿着拦截链向上传递。

源码流程

初始化一个OkHttpClient对象

OkHttpClient mOkHttpClient = new OkHttpClient();
public OkHttpClient() {      this(new Builder());  }

Builder是OkHttpClient的一个内部类,目的是构造和封装数据

public Builder() {       dispatcher = new Dispatcher();       protocols = DEFAULT_PROTOCOLS;       connectionSpecs = DEFAULT_CONNECTION_SPECS;       proxySelector = ProxySelector.getDefault();       cookieJar = CookieJar.NO_COOKIES;       socketFactory = SocketFactory.getDefault();       hostnameVerifier = OkHostnameVerifier.INSTANCE;       certificatePinner = CertificatePinner.DEFAULT;       proxyAuthenticator = Authenticator.NONE;       authenticator = Authenticator.NONE;       connectionPool = new ConnectionPool();       dns = Dns.SYSTEM;       followSslRedirects = true;       followRedirects = true;       retryOnConnectionFailure = true;       connectTimeout = 10_000;       readTimeout = 10_000;       writeTimeout = 10_000;  }

创建一个请求

Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。

Request request = new Request.Builder()        .url(url)        .build();
public Request build() {    if (url == null) throw new IllegalStateException("url == null");    return new Request(this);  }
private Request(Builder builder) {      this.url = builder.url;      this.method = builder.method;      this.headers = builder.headers.build();      this.body = builder.body;      this.tag = builder.tag != null ? builder.tag : this;  }

接下来对应的就是请求,请求分为异步与同步,先看同步

Response  response = mOkHttpClient.newCall(request).execute();

Response是HTTP响应,它继承自Closeable(Closeable继承自AutoCloseable,AutoCloseable资源自动关闭的接口),它携带了请求、网络请求协议、返回状态码、请求头、响应体等等,其中,网络请求协议是Protocol,Protocol是一个枚举类型,包含4中类型

public enum Protocol {      HTTP_1_0("http/1.0"),      HTTP_1_1("http/1.1"),      SPDY_3("spdy/3.1"),      HTTP_2("h2");  }

Call类是一个抽象类

OkHttpClient.newCall(request);  @Override   public Call newCall(Request request) {      return new RealCall(this, request);  //RealCall extends Call}protected RealCall(OkHttpClient client, Request originalRequest) {    this.client = client;    this.originalRequest = originalRequest;    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);  }
public interface Call {
Request request(); Response execute() throws IOException; void enqueue(Callback responseCallback); void cancel(); boolean isExecuted(); boolean isCanceled(); interface Factory { Call newCall(Request request); } }

OKHttp提供了execute()方法(同步方法)和enqueue()方法(异步方法),下面我们先看看execute()方法(同步方法)

execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法。没有执行则通过分发器进行执行

@Override public Response execute() throws IOException {  synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;  }  try {      client.dispatcher().executed(this);      Response result = getResponseWithInterceptorChain();      if (result == null) throw new IOException("Canceled");      return result;  } finally {      client.dispatcher().finished(this);  }

分发器将请求加入到队列

...   private final Deque
runningSyncCalls = new ArrayDeque<>();//双端队列 ... /** Used by {@code Call#execute} to signal it is in-flight. */ synchronized void executed(RealCall call) { runningSyncCalls.add(call); }

最后通过分发器的finished()方法结束请求

void finished(RealCall call) {      finished(runningSyncCalls, call, false);  }  private 
void finished(Deque
calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); if (promoteCalls) promoteCalls(); runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); } }

所以通过上面信息可知真正发送请求的地方是getResponseWithInterceptorChain()方法

真正发送请求源码分析

组装各种拦截器,然后发送请求

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);    return chain.proceed(originalRequest);}

拦截器接口

public interface Interceptor {
Response intercept(Chain chain) throws IOException; interface Chain { Request request(); Response proceed(Request request) throws IOException; Connection connection(); }}

在这个责任链模式中执行每一个连接器的proceed方法,然后我们逐个分析这些连接器

这里是责任链模式,由用户主动调用此方法,因此需要实现拦截的功能

这里如果有Intercepter实现已经添加,则为每一个拦截器创建一个ApplicationInterceptorChain然后递归直到将所有的链调用完成最后进入getResponse方法中。

@Override public Response proceed(Request request) throws IOException {  //If there's another interceptor in the chain, call that.  if (index < client.interceptors().size()) {  //新建一个环节,并且将索引+1,这里的request可能不再是originalRequest了,因为在拦截器中可能被修改了    Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);    //获取到对应的拦截器    Interceptor interceptor = client.interceptors().get(index);    //执行拦截器的intercept方法,参数是上面新建的环节,这个方法里面会调用chain.proceed(),递归了。    //在最深的一层调用getResponse之后,响应会一层层的往外传    Response interceptedResponse = interceptor.intercept(chain);    if (interceptedResponse == null) {      throw new NullPointerException("application interceptor " + interceptor          + " returned null");    } // 返回这一层拦截器处理用的响应    return interceptedResponse;  }  //递归到了最深的一层,拦截器都进入过了,发送httpRequest,获取到response.  return getResponse(request, forWebSocket);}

当执行完getResponse完成之后就一步步返回。

真正请求数据的方法

  1. request.newBuilder();由于是之前的请求,所以内部调用newBuilder的时候使用老请求的url,method,body,tag,headers。然后给这个requestBuilder中添加各种请求的键值对信息最后构造一个原信息+新信息的新请求。
  2. 创建一个请求引擎,每一个请求引擎代表依次请求/响应
  3. 在死循环中做的事情
    1. 如果请求被取消抛出异常
    2. 然后发送请求,读取响应,重新连接设置成false
    3. 如果请求失败则算了不请求了,但是如果是路由异常则需要恢复请求。
    4. 最后释放链接,得到响应。
    5. 如果存在下一步请求,则重定向。
Response getResponse(Request request, boolean forWebSocket) throws IOException {    // 复制请求头,并设置适合的属性。如果长度不为-1,则设置Content-Length,否则使用chunked方式传输。    // 如果对chunked不熟悉,请参考其他资料    RequestBody body = request.body();    if (body != null) {      // 根据传进来的request创建新的Builder.      Request.Builder requestBuilder = request.newBuilder();      // 设置Content-Type      MediaType contentType = body.contentType();      if (contentType != null) {        requestBuilder.header("Content-Type", contentType.toString());      }      // 判断使用何种方式传输      long contentLength = body.contentLength();      // body长度不为-1,设置Content-Length      if (contentLength != -1) {        requestBuilder.header("Content-Length", Long.toString(contentLength));        requestBuilder.removeHeader("Transfer-Encoding");      } else {        //  body 长度为 -1 ,使用chunked传输        requestBuilder.header("Transfer-Encoding", "chunked");        requestBuilder.removeHeader("Content-Length");      }      //创建新请求      request = requestBuilder.build();    }    // 创建一个新的引擎,每个引擎代表一次请求/响应对    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);    int followUpCount = 0; //重试次数    //死循环 出口:    // 1. 请求被取消    // 2. 请求有问题    // 3. 捕获到异常,且尝试恢复失败    // 4. 获取到响应,且无需重定向    // 5. 重定向次数超过最大限制    while (true) {      // 被取消的情况      if (canceled) {        engine.releaseStreamAllocation();        throw new IOException("Canceled");      }      boolean releaseConnection = true;      try {        // 发送请求        engine.sendRequest();        // 读取响应        engine.readResponse();        releaseConnection = false;      } catch (RequestException e) {        // 请求失败,请求本身有问题,或者是网络不通        throw e.getCause();      } catch (RouteException e) {        // 连接到服务器的路由发生异常,请求还没被发送        // 通过上一次连接异常恢复引擎        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);        // 如果恢复成功,将当前的引擎设置为这个恢复好的引擎        if (retryEngine != null) {          releaseConnection = false;          engine = retryEngine;          continue;        }        // 没法恢复,抛出异常        throw e.getLastConnectException();      } catch (IOException e) {        // 与服务器交互失败,这时,请求可能已经被发送        // 恢复引擎        HttpEngine retryEngine = engine.recover(e, null);        //如果恢复成功,将当前的引擎设置为这个恢复好的引擎        if (retryEngine != null) {          releaseConnection = false;          engine = retryEngine;          continue;        }        // 没法恢复,抛出异常        throw e;      } finally {        // 如果需要释放连接,则将连接释放        if (releaseConnection) {          StreamAllocation streamAllocation = engine.close();          streamAllocation.release();        }      }      // 获取响应      Response response = engine.getResponse();      // 下一步的请求,如果存在,则需要重定向      Request followUp = engine.followUpRequest();      //如果不需要重定向      if (followUp == null) {        if (!forWebSocket) {          // 释放连接          engine.releaseStreamAllocation();        }        //返回响应        return response;      }      // 如果需要重定向,关闭当前引擎      StreamAllocation streamAllocation = engine.close();      // 如果超过最大数,释放连接,并抛出异常      if (++followUpCount > MAX_FOLLOW_UPS) {        // 释放连接        streamAllocation.release();        // 抛出异常        throw new ProtocolException("Too many follow-up requests: " + followUpCount);      }      // 如果重定向的地址和当前的地址一样,则不需要释放连接      if (!engine.sameConnection(followUp.url())) {        streamAllocation.release();        streamAllocation = null;      }      // 使用重定向后的请求,重新实例一个引擎,开始下一次循环      request = followUp;      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,          response);    }  }

最后HttpEngine进行的操作与http协议很大相关,作者对此不感兴趣,本文只是对流程做大概分析

哥哥若是喜欢可移驾公众号:码老板

这里写图片描述

你可能感兴趣的文章
关于线程池任务报错的处理与理解
查看>>
一次数据库与es程序优化的过程
查看>>
MySQL 修改表结构相关
查看>>
三目运算法中隐藏的问题
查看>>
JVM GC问题排查(多数是内存泄漏导致)
查看>>
JVM理论
查看>>
JVM内存模型简述
查看>>
从一个Java对象看JVM内存模型及对象生命周期
查看>>
为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较?
查看>>
JDK常用监控指令和监控工具分析及OutOfMemory异常分析
查看>>
使用线程池注意事项
查看>>
java线程池理论知识拾遗
查看>>
原码反码补码,位运算,或与非
查看>>
IntelliJ IDEA普通项目添加pom.xml,转变为Maven项目
查看>>
ThreadPoolExecutor部分方法解读
查看>>
Java8 新特性 lambda表达式
查看>>
ArrayList与LinkedList的性能差别
查看>>
Tomcat类加载机制
查看>>
for(;;) and while(true)
查看>>
CountDownLatch:闭锁
查看>>