风花散却隽世间,雪月寥落化雨田

举世誉之而不加劝,举世非之而不加沮,定乎内外之分,辩乎荣辱之境,斯已矣

Okhttp的源码解读

重要的类

类名 描述
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<RealCall> 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 <T> void finished(Deque<T> 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协议很大相关,作者对此不感兴趣,本文只是对流程做大概分析

哥哥若是喜欢可移驾公众号:码老板
这里写图片描述

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011311586/article/details/79853226
个人分类: Android应用开发
想对作者说点什么? 我来说一句

jQuery源码解读

2010年05月24日 147KB 下载

jmesa的源码解读(共五部分)

2009年01月13日 24KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭