OkHttp3源码解析--Call同步请求

本文详细解析了OkHttp中同步请求的执行流程,包括创建OkHttpClient、Request和Call对象,以及通过Call对象的execute方法进行同步请求的具体实现。文章深入探讨了Dispatcher在同步请求过程中的作用,以及其对任务队列的管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面创建了Call对象,接下来就是执行。执行又分为了同步执行和异步执行,先看同步执行过程。

还是之前的过程:

//步骤1:创建OkHttpClient对象
OkHttpClient client = new OkHttpClient.Builder()
  .readTimeout(5, TimeUnit.SECONDS)
  .build();
//步骤2:创建Request对象
Request request = new Request.Builder()
    .url("http://www.baidu.com")
    .get()
    .build();
//步骤3:创建Call对象
Call call = client.newCall(request);
//步骤4:进行请求
//异步请求方式
call.enqueue(new Callback() {
  @Override
  public void onFailure(Call call, IOException e) {
    System.out.println("Fail");
  }
  @Override
  public void onResponse(Call call, Response response) throws IOException {
    System.out.println(response.body().string());
  }
});
//同步请求方式
Response response = call.execute();

通过Call对象的execute方法来完成同步请求,这个方法会一直阻塞至请求结果返回,或者请求过程出现错误。

同步请求–NewCall#execute方法的实现

@Override public Response execute() throws IOException {
    synchronized (this) {//锁住这个Call对象
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;//看是否执行过,每一个Call只能执行一次
    }
    captureCallStackTrace();
    eventListener.callStart(this);//回调请求执行事件监听器
    try {
      client.dispatcher().executed(this);//加入到Dispatcher对象的runningSyncCalls同步任务队列中
      Response result = getResponseWithInterceptorChain();//阻塞,执行一系列拦截器,完成请求,返回结果
      if (result == null) throw new IOException("Canceled");//返回结果为null的话,就是这个请求在后续的处理过程中被取消了。
      return result;//返回结果
    } catch (IOException e) {
      eventListener.callFailed(this, e);//事件监听回调
      throw e;
    } finally {
      client.dispatcher().finished(this);//完成当前请求,将任务从同步任务队列中删除。
    }
  }

过程简述:

  • 判断是否已经能执行过,如果已经执行过,直接抛异常,停止请求;

  • 回调事件监听callStart;

  • 调用Dispatcher的executed方法,将任务添加到同步任务队列中;

  • 阻塞,调用getResponseWithInterceptorChain方法,执行拦截器链,完成请求,返回结果

  • 判断请求结果,看是否请求被取消了;

  • 再次调用Dispatcher中的finished方法,将当前Call从同步任务队列中删除,当前请求Call完成。

这个过程中有Dispatcher的参与,看一下它的相关代码:

Dispatcher

public final class Dispatcher {
  private int maxRequests = 64;//最大请求数量,包括同步请求和异步请求,从下面的三种队列中相加进行统计
  private int maxRequestsPerHost = 5;//访问同一个主机的请求最大数量
  private @Nullable Runnable idleCallback;//空闲时回调,空闲指的时正在执行同步和异步的Call个数为0的时候/** 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<>();//准备被执行的异步Call-->AsyncCall
  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在执行的异步Call-->AsyncCall
  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//正在执行的同步Call-->RealCallpublic Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }public Dispatcher() {
  }
  //创建线程池
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      //线程池中核心线程为0,最大线程数可以为Integer.MAX_VALUE,空闲存活时间为60S
      //即这个线程池的特点是,来一个请求可以创建一个新的线程。但是线程数也有限制,上面的maxRequests可以限制最大的线程数量不能超过64.
      //响应速度快。
      //空闲60S后会回收所有线程的资源,也可以节省资源。
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }
  //Realcall中的client.dispatcher().executed(this);执行到这
  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);//将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) {
    //第三个参数promoteCalls:同步请求时为false,异步为true。
    finished(runningSyncCalls, call, false);
  }//calls异步和同步对应的任务队列
  //call 正在进行的call
  //promoteCalls 同步请求是为false   异步请求时为true
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;//正在执行的Call的数量
    Runnable idleCallback;//空闲回调
    synchronized (this) {
      //删除当前Call
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      if (promoteCalls) promoteCalls();//同步请求不执行,异步请求执行
      runningCallsCount = runningCallsCount();//统计正在执行的Call的数量,包括同步请求个异步请求;
      idleCallback = this.idleCallback;
    }if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }}

可以看到,在同步请求的时候:

首先调用executed(RealCall call)方法,将RealCall对象添加到runningSyncCalls队列中;

其实也就是添加到队列中,后续对请求的执行,不会从同步队列中获取任务,而是直接对上述的RealCall进行操作。这里的队列其实就是为了多个任务的进度控制。

然后,任务执行完之后,执行finished方法,将当前任务从队列中删除。就可以了。

OkHttp 是一个广泛应用于 Android 开发中的网络请求框架,其设计精巧、性能优越,内部实现涉及多个核心组件和机制。以下是对 OkHttp源码分析及其工作原理的详细解析。 ### 核心类与架构 OkHttp 的架构由多个关键类组成,包括 `OkHttpClient`、`Request`、`Call`、`Dispatcher`、`ConnectionPool` 等。这些类协同工作,确保网络请求的高效执行。 - **OkHttpClient**:作为客户端的入口点,负责创建和管理 `Call` 对象。它通过建造者模式构建,允许用户自定义配置,如超时时间、拦截器链等。 - **Request**:表示 HTTP 请求,包含 URL、方法、头部信息等。 - **Call**:代表一次具体的网络请求,`RealCall` 是其实现类,负责实际的请求处理。 - **Dispatcher**:任务调度器,管理异步请求的执行,使用线程池来处理并发请求[^4]。 - **ConnectionPool**:连接池,用于复用 HTTP 连接,减少建立新连接的时间开销。默认情况下,OkHttp 使用一个清理线程池定期检查并移除空闲连接[^1]。 ### 请求流程 OkHttp请求流程可以分为以下几个步骤: 1. **构建请求**:通过 `OkHttpClient.Builder` 构建 `OkHttpClient` 实例,并设置相关参数。接着,使用 `Request.Builder` 创建 `Request` 对象,指定 URL、方法、头部等信息。 2. **发起请求**:调用 `OkHttpClient.newCall(request)` 创建 `Call` 对象,然后根据需求选择同步或异步请求方式: - **同步请求**:直接调用 `execute()` 方法,阻塞当前线程直到响应返回。 - **异步请求**:调用 `enqueue(Callback responseCallback)` 方法,将请求加入队列并在后台线程中执行,完成后回调 `onResponse` 或 `onFailure`。 3. **拦截器链**:无论是同步还是异步请求,最终都会调用 `getResponseWithInterceptorChain()` 方法,从拦截器链 `interceptors` 中获取结果。拦截器链包括重试、桥接、缓存、连接、网络五个阶段,每个阶段都可以对请求和响应进行处理[^4]。 ### 缓存机制 OkHttp 提供了内置的缓存支持,可以通过 `Cache` 类设置缓存目录和大小。当启用缓存后,OkHttp 会自动存储和读取响应数据,从而减少不必要的网络请求,提高应用性能[^1]。 ### 协议支持 OkHttp 支持多种协议,包括 HTTP/1.0、HTTP/1.1 和 HTTP/2。`protocols` 字段定义了 OkHttp 支持的协议列表,默认情况下,OkHttp 优先使用 HTTP/2。通过 `java.net.URI.getScheme()` 可以获取 URL 的协议(如 http、https),而 `Protocol.get()` 则提供了更具体的协议版本信息(如 http/1.0、http/1.1、h2)[^5]。 ### 示例代码 以下是一个简单的 OkHttp 同步请求示例: ```java OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://api.example.com/data") .build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { System.out.println(response.body().string()); } } catch (IOException e) { e.printStackTrace(); } ``` 对于异步请求,可以使用如下代码: ```java OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url("https://api.example.com/data") .build(); 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 { if (response.isSuccessful()) { System.out.println(response.body().string()); } } }); ``` ### 总结 OkHttp 以其高效的连接管理和灵活的拦截器机制,成为 Android 开发中最受欢迎的网络请求框架之一。通过对 `Dispatcher`、`ConnectionPool`、`Cache` 等核心组件的理解,开发者可以更好地优化网络请求,提升应用性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值