阅读 1571

OkHttp 开源库使用与源码解析

关于 OkHttp

OkHttp 是一个适用于 Android 和 Java 应用程序的 HTTP+HTTP/2客户端。

如何使用

Android 开发只需添加依赖,如下:
implementation("com.squareup.okhttp3:okhttp:3.13.1")

官方示例1:获取一个 url 上的内容并输出

//Http 客户端
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
    //构造请求
  Request request = new Request.Builder()
      .url(url)
      .build();
  //执行请求,获取数据
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
复制代码

官方示例2:给服务器post数据

public static final MediaType JSON
    = MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  try (Response response = client.newCall(request).execute()) {
    return response.body().string();
  }
}
复制代码

简单分析一下这段代码:这个post请求操作看起来很简单。但是我们需要学习其中几个很重要的接口:

OKHttpClient:它代表着 http 客户端
Request:它封装了请求对象,可以构造一个 http 请求对象
Response:封装了响应结果
Call:client.newCall()调用后生成一个请求执行对象Call,它封装了请求执行过程。

下面我们结合这个例子来分析源码:

newCall().execute()

跟踪源码后发现这个方法是在 Call 中的接口,代码如下:

/**
 * A call is a request that has been prepared for execution. A call can be canceled. As this object
 * represents a single request/response pair (stream), it cannot be executed twice.
 */
public interface Call extends Cloneable {
    //省略部分代码
    
    //同步执行请求
    Response execute() throws IOException;
    
    //请求加入队列
    void enqueue(Callback responseCallback);
    
    //省略部分代码
}
复制代码

Call 的实现类是 RealCall,继续追踪源码的 RealCall.java 文件,可以看到 execute 方法:

@Override public Response execute() throws IOException {
    //同步锁检查该请求是否已经执行,如果没有则标记executed = ture,否则抛出异常
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    
    captureCallStackTrace();
    timeout.enter();
    //调用了回调方法 callStart
    eventListener.callStart(this);
    try {
      //okhttp 客户端调用 dispatcher 将执行请求对象
      client.dispatcher().executed(this);
      //调用了 getResponseWithInterceptorChain 方法获取到响应数据 Response,后期还会继续分析
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      //请求失败的回调 callFailed
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
      //使用 dispather 对象调用 finished 方法,完成请求
    }
  }
复制代码

接下来我们详细分析一下 dispatcher.execute 和 getResponseWithInterceptorChain 这两个方法:

跟踪源码 Dispatcher

public final class Dispatcher {
    //省略部分代码
  /** Executes calls. Created lazily. */
  private @Nullable 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<>();

  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  void enqueue(AsyncCall call) {
    synchronized (this) {
      readyAsyncCalls.add(call);

      // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
      // the same host.
      if (!call.get().forWebSocket) {
        AsyncCall existingCall = findExistingCallWithHost(call.host());
        if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
      }
    }
    promoteAndExecute();
  }
  
  private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }
    boolean isRunning = promoteAndExecute();
    if (!isRunning && idleCallback != null) {
      idleCallback.run();
    }
  }

}
复制代码

发现 Dispatcher 是一个调度器,它的作用是对请求进行分发。它的内部有三个队列,分别是同步请求进行队列、异步请求等待队列、异步请求执行队列。

核心方法: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:

RealInterceptorChain

该类主要负责将所有的拦截器串连起来,使所有的拦截器以递归的方式进行实现,从而确保只有所有的拦截器都执行完之后才会返回 Response。以下对该类中的主要方法 proceed 进行讲解:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection 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.httpCodec != null && !this.connection.supportsUrl(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.httpCodec != null && calls > 1) {
      throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
          + " must call proceed() exactly once");
    }
    //生成下一个拦截器调动该方法的 RealInterceptorChain 对象,其中 index+1 用于获取下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, 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 (httpCodec != 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;
  }
复制代码

总结

以上就是我们对 OkHttp 核心源码的分析。当我们发起一个请求的时候会初始化一个 Call 的实例,然后根据同步和异步的不同,分别调用它的 execute() 和 enqueue() 方法。大致过程都是通过拦截器组成的责任链,依次经过重试、桥接、缓存、连接和访问服务器等过程,来获取到一个响应并交给用户。

文章分类
阅读
文章标签