androidAndroid开发程序员

OkHttp3.0-源码分析

2017-12-12  本文已影响402人  负了时光不负卿

1. OkHttp官网介绍:

OkHttp: An HTTP+HTTP/2 client for Android and Java applications.
该库支持 HTTP1.0、HTTP1.1、HTTP2.0 以及 SPDY ,都在类Protocol 中声明。

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="https://tools.ietf.org/html/rfc7230">RFC
   * 7230</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>Current versions of OkHttp do not support this protocol.
   *
   * @deprecated OkHttp has dropped support for SPDY. Prefer {@link #HTTP_2}.
   */
  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");
}

2. OkHttp的基本使用:

(1) Step One: 构建OkHttpClient对象

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

(2) Step Two: 构建Request对象

Request request = new Request.Builder().url("xxxxx").build();

(3) Step Three: 通过上两步创建的对象生成Call

Call newCall = okHttpClient.newCall(request);

(4) Step Four: 使用Call发送异步或同步请求,获取Response对象。

 //  同步请求:
Response response = newCall.execute();

//  异步请求:
 newCall.enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {
           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {

           }
       });

(5) 取消网络请求:

newCall.cancel();

(6) 注意事项:

  1. OkHttp发送异步请求,CallBack回调依旧执行在子线程,所以不能直接进行UI更新操作。
  2. 同一个Call只能执行一次同步或者异步网络请求。

3. OkHttp流程图

okhttp网络请求流程图.PNG

从整体来看,我们通过构建OkHttpClient对象,并调用其newCall (Request) 方法生成一个真正用于执行网络请求的Call实例。call.execute()进行同步网络请求,call.enqueue()进行异步网络请求。但不管是同步还是异步,在网络请求前,先将这个请求放入到dispatcher的请求队列中,然后getResponseWithInterceptorChain()来链式调用各拦截器(如下图所示)获取Response对象,最后将这次请求从队列中移除。

请求链式调用流程图.PNG

4. 核心代码分析

4.1 OkHttpClient创建(Builder模式):

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

    public Builder() {
      dispatcher = new Dispatcher(); 
       ...
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
    public OkHttpClient build() {
      return new OkHttpClient(this);
    }
OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
   ...
}

其中在Builder构造器内创建了Dispatcher对象,最终通过Builder.build()将Dispatcher传递给OkHttpClient,所以我们一定要记住OkHttpClient中存在Dispatcher对象。当然,既然OkHttpClient采用Builder模式创建实例,就允许我们以链式调用的方式对OkHttpClient进行配置,正如下面所示,但这不是本文关注的重点。

4.2 Request创建(Builder模式):

Request request = new Request.Builder().url("xxxxx").build();

和上面创建OkHttpClient一样,依旧Builder模式允许使用者灵活配置请求。

4.3 Call对象生成:

Call newCall = okHttpClient.newCall(request);

OkHttpClient.class 中的newCall() 方法:

 @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

RealCall.class 中的newRealCall() 方法:

  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    ... 
    return call;
  }

到此处为止,我们通过OkHttpClient.newCall(Request)生成一个newRealCall对象,这个对象包含了OkHttpClient和Request引用,所以我们完全可以在RealCall类中做剩余工作了,而事实也正是如此。

4.4 开始同步(异步)网络请求:

RealCall 的同步方法:

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");  // 这里可以解释为什么每个call只能执行一次。
      executed = true;
    } 
    try {
      client.dispatcher().executed(this);           // 将RealCall存到之前强调的OkHttpClient的dispatcher中
      Response result = getResponseWithInterceptorChain(); // 真正向服务器发送网络请求的代码,后面会具体说明。
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) { }
      finally {
      client.dispatcher().finished(this);     // 执行请求后将RealCall从dispatcher中移除
    }
  }

同步请求,通过dispatcher对RealCall储存和移除逻的辑相当简单,只是维护了一个集合用于管理。

Dispatcher.class中的同步请求的添加和移除方法:

public final class Dispatcher {
  ...
   // 同步请求集合
   private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

   // 添加同步请求
   synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
   }
 
   // 移除同步请求
   void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
   }

   private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    ...
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();  // 同步请求,此处不会被调用。
    }
    ...
   }
  ...
 }

RealCall的异步方法:

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");  // 这里可以解释为什么每个call只能执行一次。
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback));  // AsyncCall为RealCall的内部类,实现Runnable接口,用于线程池的调度。
  }

先不讲client.dispatcher().enqueue(xx)具体代码实现,我们先看一下AsyncCall的结构。

final class RealCall implements Call {
    ... 
    final class AsyncCall extends NamedRunnable {
        AsyncCall(Callback responseCallback) {
           super("OkHttp %s", redactedUrl());
           this.responseCallback = responseCallback;
        }
        
       @Override protected void execute() { ... }
    }
}
 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作为RealCall的内部类,AsyncCall引用RealCall的实例对象,同时AsyncCall实现了Runnable接口,一旦开始执行就会调用AsyncCall 的execute()方法。知道了AsyncCall 的基本结构,就可以看client.dispatcher().enqueue(new AsyncCall(responseCallback)) 内部具体实现了。

public final class Dispatcher {

 private int maxRequests = 64;  // 同时进行的异步网络请求最大数

  private int maxRequestsPerHost = 5;  // 同一个网络请求主机地址允许最大请求个数

  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();  // 异步请求缓存队列

  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();  // 异步请求执行队列

  public synchronized ExecutorService executorService() {   // 获取线程池执行器
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

  synchronized void enqueue(AsyncCall call) {   // 添加异步请求。
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {  // 判断能否放入异步网络请求执行队列
      runningAsyncCalls.add(call);  // 将异步请求添加到执行队列
      executorService().execute(call);  // 执行异步网络请求
    } else {
      readyAsyncCalls.add(call); // 将异步请求添加到等待队列
    }
  }

 ...
}

执行异步网络请求交由ThreadPoolExecutor处理,执行runnable的run方法,之前先看过了AsyncCall的结构,runable的具体实现是通过AsyncCall的execute()方法处理的,具体代码如下:

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();            // 真正向服务器发送网络请求的代码,后面会具体说明。
        if (retryAndFollowUpInterceptor.isCanceled()) {                     // 调用了call.cancel()方法取消网络请求
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled")); // 通过CallBack进行失败的回调
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);    // 通过CallBack进行成功的回调
        }
      } catch (IOException e) {
        ... 
      } finally {
        client.dispatcher().finished(this);    // 异步请求结束后,从执行队列中移除请求
      }
    }

异步请求的移除操作

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


  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    ... 
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");    //对执行队列中移除请求
      if (promoteCalls) promoteCalls();    // 将异步请求准备队列中的将请求放入执行队列中,做补位操作
      ...
    }
   ... 
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; 
    if (readyAsyncCalls.isEmpty()) return; 

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

5. 链式调用发送网络请求

之前只是知道通过getResponseWithInterceptorChain() 真正向服务器发送网络请求,但是没有做具体的分析,因为这块相对来说比较独立,可以单独提出来讲,废话不多说,先具体看看该方法内部实现。

  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 (!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);
  }

RealInterceptorChain中的proceed()方法:

 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {

     ... 
   //  1.  获取获截器链中的第一个拦截器
   //  2.  通过index + 1,去掉拦截器链中的第一个拦截器获得新的拦截器链
   //  3.  调用原拦截器链中第一个拦截器的intercept()方法,并传入新的拦截器链

     RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);
    ... 
    return response;
  }

这里面的具体代码实现简单粗暴,无非是按顺序添加不同的拦截器,用于分级处理Request和Response,最后创建了一个RealInterceptorChain对象,用于顺序执行每个拦截器中的intercept()方法。

接着看其中一个拦截器RetryAndFollowUpInterceptor中intercept()方法

@Override public Response intercept(Chain chain) throws IOException {

      Request request = chain.request();

      ...   //  加工处理网络请求体

      response = realChain.proceed(request, streamAllocation, null, null);  // 将请求传递给下一个拦截器

      ...  //   加工处理响应体

     return response; 
 }

可以看到每个拦截器做的事无非是加工请求对象,将请求交由下一个拦截器处理,当然最后一个拦截器就不需要下交请求,而是直接向服务器发送网络请求,最后对响应加工处理并返回。

上一篇下一篇

猜你喜欢

热点阅读