源码分析OKHttp的执行过程

栏目: Java · 发布时间: 6年前

内容简介:它的项目地址为:先从一个简单的官方示例来看,这是一个同步

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

它的项目地址为: github.com/square/okht…

0x00 简单使用

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

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 中的接口

/**
 * 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 方法

@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 方法,完成请求

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

dispatcher.execute
getResponseWithInterceptorChain

接下来分别看这两个方法

0x02 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<>();
  //...

  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 最关键的部分了

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

@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 接口

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 异步的情况

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 方法

@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 方法

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 方法

@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 中的关键的流程是通过拦截器责任链来完成的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

爆品战略

爆品战略

金错刀 / 北京联合出版公司 / 2016-7-1 / 56.00

◆ 划时代的商业著作!传统企业转型、互联网创业的实战指南! ◆ 爆品是一种极端的意志力,是一种信仰,是整个企业运转的灵魂! ◆ 小米创始人雷军亲自作序推荐!小米联合创始人黎万强、分众传媒创始人江南春、美的董事长方洪波、九阳董事长王旭宁等众多一线品牌创始人联袂推荐! ◆ 创图书类众筹新纪录!众筹上线2小时,金额达到10万元;上线1星期,金额突破100万元! ◆ 未售......一起来看看 《爆品战略》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器