前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码分析OKHttp的执行过程

源码分析OKHttp的执行过程

作者头像
阳仔
发布2019-07-30 15:22:41
5440
发布2019-07-30 15:22:41
举报
文章被收录于专栏:终身开发者

OKHttp 是目前 Android 平台主流的网络请求的基础框架。因此我们有必要对其源码进行阅读学习,了解其内部的原理、项目结构、以及请求的执行过程。

它的项目地址为:https://github.com/square/okhttp

0x00 简单使用

先从一个简单的官方示例来看,这是一个同步 GET 请求

代码语言:javascript
复制
public class GetExample {
  //1.http客户端
  OkHttpClient client = new OkHttpClient();

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

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
    System.out.println(response);
  }
}

可以看出这个 GET 请求操作是很简单的。有几个很重要的接口

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

这几个接口是程序员在使用 OKHttp 库中经常遇到的。

接下来将从这个示例开始阅读 OkHttp 的源码

0x01 Call.execute()

跟进源码后发现这个方法是在 Call 中的接口

代码语言:javascript
复制
/**
 * 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

Call 的实现类是 RealCall,因此 execute 方法

代码语言:javascript
复制
@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

这个方法也不是很长,逻辑很简单:

  • 同步锁检查该请求是否已经执行,如果没有则标记 executed=ture,否则抛出异常
  • 调用了回调函数 callStart
  • okhttp客户端调用 dispatcher 将执行请求对象
  • 调用了 getResponseWithInterceptorChain 方法获取到响应数据 Response,这个方法很重要,后面会继续跟进
  • 然后是对请求失败的回调 callFailed
  • 最后还是使用 dispather对象调用 finished方法,完成请求

这里的逻辑还是比较清晰的,出现两个重要的方法

  1. dispatcher.execute
  2. getResponseWithInterceptorChain

接下来分别看这两个方法

0x02 Dispatcher
代码语言:javascript
复制
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<>();
  //...

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  /** Used by {@code AsyncCall#run} to signal completion. */
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }

  /** Used by {@code Call#execute} to signal completion. */
  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();
    }
  }
}

可以看出 Dispatcher 是一个调度器,它内部有一个线程池 executorService ,还有三个队列,分别代表同步请求进行队列、异步请求等待队列、异步请求执行队列。

我们发现调用 execute方法时就是将 Call对象加入到同步请求进行队列 runningSyncCalls中,而调用 finished 方法则是将 Call请求从队列中移除

0x03 getResponseWithInterceptorChain

现在在回到 RealCall 源码中,这个方法可以说是 OkHttp最关键的部分了

代码语言:javascript
复制
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()));//处理cookie的拦截器
    interceptors.add(new CacheInterceptor(client.internalCache()));//处理缓存的拦截器
    interceptors.add(new ConnectInterceptor(client));//负责连接的拦截器
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());//添加程序员自定义的network拦截器
    }
    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 包含了所有的拦截器对象。然后调用 chain.proceed方法开始执行请求,这时就到了 RealInterceptorChain 这个类中。

0x04 RealInterceptorChain
代码语言:javascript
复制
@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

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

    calls++;

    //省略无关代码...

    //1. 执行拦截器责任链中的下一个拦截器
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    //2. 获取当前的拦截器
    Interceptor interceptor = interceptors.get(index);
    //3. 执行拦截,并返回响应
    Response response = interceptor.intercept(next);

    //省略...

    return response;
  }

可以看到,在 proceed方法,又构造了 RealInterceptorChain并且调用了 interceptor.intercept方法,

而这个方法中又会调用 next.proceed方法,直至返回 response。这个过程有点像递归调用。

0x05 Interceptor

拦截器,它是一个接口,内部还有一个 Chain接口

代码语言:javascript
复制
public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();

    Call call();

    int connectTimeoutMillis();

    Chain withConnectTimeout(int timeout, TimeUnit unit);

    int readTimeoutMillis();

    Chain withReadTimeout(int timeout, TimeUnit unit);

    int writeTimeoutMillis();

    Chain withWriteTimeout(int timeout, TimeUnit unit);
  }
}

所有的拦截器都需要实现这个接口。

0x06 异步的情况
代码语言:javascript
复制
public final class AsynchronousGet {
  private final OkHttpClient client = new OkHttpClient();

  public void run() throws Exception {
    Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
    //调用enqueue方法,并设置回调接口
    client.newCall(request).enqueue(new Callback() {
      @Override public void onFailure(Call call, IOException e) {
        e.printStackTrace();
      }

      @Override public void onResponse(Call call, Response response) throws IOException {
           //这里获取到响应结果数据
      }
    });
  }

然后我们再看 RealCall中的 enqueue方法

代码语言:javascript
复制
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    //最终执行了dispatcher的enqueue方法
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

其实是执行了 dispatcher中的 enqueue方法

代码语言:javascript
复制
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

dispatcher中通过线程池来执行 AsyncCall对象,因此跟进到 AsyncCall中的 execute方法

代码语言:javascript
复制
@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        //最终还是调用了getResponseWithInterceptorChain()!!!
        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 {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

发现最终还是执行了 getResponseWithInterceptorChain,因此不管是同步还是异步、最终的流程还是一样。

0x07 总结
  1. OKHttpClient

这是一个 http 客户端。构建很简单,可以使用无参构造函数。其内部是通过 Builder 对象进行构建的。也可以通过其内部静态类 Builder 来构建,然后通过 builder 设置 OkHttpClient 构造参数。

  1. Request

请求对象。其内部也是使用 Builder 模式封装了构造的过程,通过 Builder使用链式调用也是目前很多开源库中常见的模式。

  1. Response

响应结果。客户端执行后返回响应结果,通过 Response 可以很方便的获取到响应数据。

  1. Call

请求执行。可以执行同步或者异步的请求,分别将请求发送到 dispatcher

  1. Dispatcher

调度器。其内部有一个线程池,并维护了三个队列:同步进行请求队列、异步请求等待队列、异步请求进行队列。

还有两个重要的方法 executeenqueue方法,分别代表同步、异步的方法。这两个方法的最终的执行流程都是一样的

  1. Interceptor

拦截器。拦截器在 OKHttpClient中使是用责任链模式来实现的。 Okhttp 中的关键的流程是通过拦截器责任链来完成的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 终身开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 简单使用
  • 0x01 Call.execute()
    • RealCall
    • 0x02 Dispatcher
    • 0x03 getResponseWithInterceptorChain
    • 0x04 RealInterceptorChain
    • 0x05 Interceptor
    • 0x06 异步的情况
    • 0x07 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档