Android OkHttp(三)源码解析

    前面两篇文章,我们介绍了OKHttp的基本用法,今天这篇文章将从OkHttp源码的角度来分析OkHttp的整个工作流程。

初始化一个OkHttpClient对象,

    OkHttpClient mOkHttpClient = new OkHttpClient();

OkHttpClient的构造函数中又初始化了一个Builder对象,

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

Builder的构造函数中,默认会创建对象以及一些属性,Builder对象是用来构造网络请求的。这种设计模式和Android的对话框中的Builder几乎是一个意思。

 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 request = new Request.Builder()  
      .url(url)  
      .build(); 
Request类是HTTP请求,它携带了请求地址、请求方法、请求头部、请求体以及其他信息。它也是通过Builder模式创建的。Request.Builder().build()源码,

    public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }
Request的构造方法,

   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 {
  /**
   * An obsolete plaintext framing that does not use persistent sockets by default.
   */
  HTTP_1_0("http/1.0"),

  /**
   * A plaintext framing that includes persistent connections.
   *
   * <p>This version of OkHttp implements <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC
   * 2616</a>, and tracks revisions to that spec.
   */
  HTTP_1_1("http/1.1"),

  /**
   * Chromium's binary-framed protocol that includes header compression, multiplexing multiple
   * requests on the same socket, and server-push. HTTP/1.1 semantics are layered on SPDY/3.
   *
   * <p>This version of OkHttp implements SPDY 3 <a
   * href="http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1">draft 3.1</a>. Future
   * releases of OkHttp may use this identifier for a newer draft of the SPDY spec.
   */
  SPDY_3("spdy/3.1"),

  /**
   * The IETF's binary-framed protocol that includes header compression, multiplexing multiple
   * requests on the same socket, and server-push. HTTP/1.1 semantics are layered on HTTP/2.
   *
   * <p>HTTP/2 requires deployments of HTTP/2 that use TLS 1.2 support {@linkplain
   * CipherSuite#TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} , present in Java 8+ and Android 5+. Servers
   * that enforce this may send an exception message including the string {@code
   * INADEQUATE_SECURITY}.
   */
  HTTP_2("h2");
  
  }
接着看

OkHttpClient.newCall(request);

Call类是一个准备执行的请求,它可以被取消,它代表着一个单一的请求/响应流,不能被执行两次。Call类是一个接口,

public interface Call {
  /** Returns the original request that initiated this call. */
  Request request();

  /**
   * Invokes the request immediately, and blocks until the response can be processed or is in
   * error.
   *
   * <p>The caller may read the response body with the response's {@link Response#body} method. To
   * avoid leaking resources callers must {@linkplain ResponseBody close the response body}.
   *
   * <p>Note that transport-layer success (receiving a HTTP response code, headers and body) does
   * not necessarily indicate application-layer success: {@code response} may still indicate an
   * unhappy HTTP response code like 404 or 500.
   *
   * @throws IOException if the request could not be executed due to cancellation, a connectivity
   * problem or timeout. Because networks can fail during an exchange, it is possible that the
   * remote server accepted the request before the failure.
   * @throws IllegalStateException when the call has already been executed.
   */
  Response execute() throws IOException;

  /**
   * Schedules the request to be executed at some point in the future.
   *
   * <p>The {@link OkHttpClient#dispatcher dispatcher} defines when the request will run: usually
   * immediately unless there are several other requests currently being executed.
   *
   * <p>This client will later call back {@code responseCallback} with either an HTTP response or a
   * failure exception.
   *
   * @throws IllegalStateException when the call has already been executed.
   */
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
  boolean isExecuted();

  boolean isCanceled();

  interface Factory {
    Call newCall(Request request);
  }
}
newCall()方法是创建一个Call对象,

    @Override 
    public Call newCall(Request request) {
    return new RealCall(this, request);
  }
RealCall类实现了Call类,下面展示RealCall()方法的代码,
    protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);
  }

接着是RealCall的execute()方法。execute()方法是同步方法,即一直等待http请求, 直到返回了响应. 在这之间会阻塞进程, 所以通过同步方法不能在Android的主线程中执行, 否则会报错。

OKHttp提供了execute()方法(同步方法)和enqueue()方法(异步方法),下面我们先看看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);
    }
  }

execute()方法,首先判断是否已执行过,如果已经执行过,则抛出异常信息,也就是说一次Call实例只能调用一次execute()方法,和我们之前说的一样。

如果未执行,则调用Dispatcher类的executed()方法将该Call加入到一个双端队列中,

   ...
   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);
  }

 接着调用getResponseWithInterceptorChain()方法返回Response对象,最后finally中,调用Dispatcher的finished()方法,在从已经执行的双端队列中移除本次Call。

 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() throws IOException {
    // Build a full stack of interceptors.
    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 (!retryAndFollowUpInterceptor.isForWebSocket()) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(
        retryAndFollowUpInterceptor.isForWebSocket()));

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
仔细查看便会发现,该方法是组装各种拦截器为一个拦截器链,最后调用RealInterceptorChain的proceed()方法,来处理这个请求,

 @Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpStream, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
      Connection connection) throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();

    calls++;

    // If we already have a stream, confirm that the incoming request will use it.
    if (this.httpStream != null && !sameConnection(request.url())) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must retain the same host and port");
    }

    // If we already have a stream, confirm that this is the only call to chain.proceed().
    if (this.httpStream != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpStream, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    // Confirm that the next interceptor made its required call to chain.proceed().
    if (httpStream != null && index + 1 < interceptors.size() && next.calls != 1) {
      throw new IllegalStateException("network interceptor " + interceptor
          + " must call proceed() exactly once");
    }

    // Confirm that the intercepted response isn't null.
    if (response == null) {
      throw new NullPointerException("interceptor " + interceptor + " returned null");
    }

    return response;
  }
拦截器Interceptor和拦截器链Chain都是接口,

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

下面用一张流程图来说明拦截器链递归从拦截器中返回Response(这个过程很类似Android中的事件处理机制)


下面再看看enqueue()方法(异步方法),指在另外的工作线程中执行http请求, 请求时不会阻塞当前的线程, 所以可以在Android主线程中使用。

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }
该方法和同步方法一样,首先都校验这个Call是否已经被执行,如果执行过,就报异常。如果为执行,则调用Dispatcher分发器的enqueue()方法。首先,我们看看AsyncCall这个类,

 final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

    private AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl().toString());
      this.responseCallback = responseCallback;
    }

    String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }

    RealCall get() {
      return RealCall.this;
    }

    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}
AsyncCall–实际上是一个Runnable,在run()方法中调用了execute()方法,在该方法中,我们又看到既熟悉又陌生的代码(getResponseWithInterceptorChain()),

 Response response = getResponseWithInterceptorChain();
执行该方法后,根据响应结果设置回调方法的结果。AsyncCall执行完调用Dispatcher的finished(this)方法,

 void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

 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();
    }
  }
finished()方法先从runningAsyncCalls(异步请求队列)删除已经执行的异步请求,然后接着调用了promoteCalls()方法,

 private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }
首先判断runningAsyncCalls(异步请求队列)是否还有请求,如果有,则返回;否则readyAsyncCalls(异步调用准备任务)是否为空,如果为空,则返回;否则,循环readyAsyncCalls(异步调用准备任务),将call加入到runningAsyncCalls(异步请求队列)中,并在readyAsyncCalls(异步调用准备任务)删除掉该call,接着线程池执行call。

下面接着看Dispatcher分发器的enqueue()方法,

 synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
如果runningAsyncCalls的大小小于最大请求数量(最大线程数量、并发数量)并且call小于最大主机请求限制,那么将call 加入到runningAsyncCalls中,接着线程池执行call;否则,将call加入到readyAsyncCalls(异步调用准备任务)。

PS: runningCallsForHost()方法,循环判断cal的hostl和runningAsyncCalls的中的call的host相同的数量。同一请求是否超过想过请求同时存在的最大值。

  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

可以看到请求的最核心代码,与Dispatcher类分不开,下面就看看Dispatcher类,Dispatcher是异步请求的策略,

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private Runnable idleCallback;

  /** Executes calls. Created lazily. */
  private ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  ...
}
  Dispatcher维护了如下变量,
    int maxRequests = 64: 最大并发请求数为64
    int maxRequestsPerHost = 5: 每个主机最大请求数为5
    Runnable idleCallback:Runnable对象,在删除任务时执行
    ExecutorService executorService:消费者池(也就是线程池)
    Deque<AsyncCall> readyAsyncCalls:缓存,异步调用准备执行任务
    Deque<AsyncCall> runningAsyncCalls:正在运行的异步任务,包含取消尚未完成的调用

    Deque<RealCall> runningSyncCalls: 正在运行的同步任务,包含取消尚未完成的调用

至此,OKHttp的源码分析就结束了!希望本篇文章对读者有所收获!微笑

下面结合本篇文章,总结OKHttp的整体流程,流程图如下所示,















  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值