okhttp3 源码解析笔记整理——同步、异步请求

系列文章:

  1. okhttp3 源码解析笔记整理——同步、异步请求
  2. okhttp3 源码解析笔记整理——拦截器与请求的实现
  3. okhttp3 读书笔记——默认的线程池,addNetworkInterceptor() 与 addInterceptor() 的区别,复用连接池

额外参考文章:
【Android】OkHttp源码分析/#CallServerInterceptor


1、同步请求

okhttp 可以使用 RealCall # execute() 方法来进行一个同步的请求。

try {
    Response response = mOkHttpClient.newCall(request).execute();
} catch (IOException e) {
    e.printStackTrace();
}

RealCall # execute() 方法如下:

@Override public Response execute() throws IOException {
  // 限制 call 只能执行一次请求
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  // 使用超时机制
  timeout.enter();
  eventListener.callStart(this);
  try {
    // 利用调度器 dispatcher 将当前请求添加到一个收集正在运行的同步请求的队列 runningSyncCalls 中
    client.dispatcher().executed(this);
    // 利用拦截器链来实现真正的请求
    Response result = getResponseWithInterceptorChain();
    if (result == null) throw new IOException("Canceled");
    return result;
  } catch (IOException e) {
    e = timeoutExit(e);
    eventListener.callFailed(this, e);
    throw e;
  } finally {
    // 最后,将当前请求从 runningSyncCalls 中移除
    client.dispatcher().finished(this);
  }
}

在上述方法中,涉及到两个重点的点。

第一个就是调度去 Dispatcher,其内部的成员变量如下:

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;
  /** 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 synchronized ExecutorService executorService() {
  if (executorService == null) {
    executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
        new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
  }
  return executorService;
}
}

其中就有 runningSyncCalls,用于收集正在运行的同步请求。而 readyAsyncCalls 用于收集准备运行的异步请求,runningAsyncCalls 用于收集正在运行的异步请求。

executorService 为异步请求时要用到的线程池。executorService() 方法中表明了其的默认实现。

第二个getResponseWithInterceptorChain() 方法,在方法中,会通过构造一系列的拦截器,来组成一个拦截器链来实现真实的请求。


2、异步请求

okhttp 实现异步请求:

mOkHttpClient.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) {
    }
});

RealCall # enqueue()

@Override public void enqueue(Callback responseCallback) {
  // 限制 call 只能执行一次请求
  synchronized (this) {
    if (executed) throw new IllegalStateException("Already Executed");
    executed = true;
  }
  captureCallStackTrace();
  eventListener.callStart(this);
  // 添加进 dispatcher 的 readyAsyncCalls 中
  // 会把 responseCallback 包装成 AsyncCall
  //(AsyncCall是 RealCall 的内部类,实现了 NamedRunnable)
  client.dispatcher().enqueue(new AsyncCall(responseCallback));
}

Dispatcher

void enqueue(AsyncCall call) {
  synchronized (this) {
    // 先将包装成 AsyncCall 的 call 添加进 readyAsyncCalls
    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) {
      // 如果不是 WebSocket,则在 runningAsyncCalls 中和 readyAsyncCalls 中寻找是否有连接相同主机的请求 existingCall
      AsyncCall existingCall = findExistingCallWithHost(call.host());
      // 如果有找到,则将 existingCall 的 callsPerHost 引用赋值给当前 call 的 callsPerHost
      // 这样做的目的是在于,对于异步请求,okhttp 会对有着相同主机的 call 在请求时进行计数,
      // 默认限制能够同时请求同一主机的 call 的最大个数为 Dispatcher # maxRequestsPerHost(5)
      // 而实现计数的关键就在 callsPerHost,它是 AtomicInteger 的实例对象
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
    }
  }
  // 调用 promoteAndExecute() 方法来推动和执行存放在 readyAsyncCalls 中的异步请求
  promoteAndExecute();
}

@Nullable private AsyncCall findExistingCallWithHost(String host) {
  for (AsyncCall existingCall : runningAsyncCalls) {
    if (existingCall.host().equals(host)) return existingCall;
  }
  for (AsyncCall existingCall : readyAsyncCalls) {
    if (existingCall.host().equals(host)) return existingCall;
  }
  return null;
}

private boolean promoteAndExecute() {
  assert (!Thread.holdsLock(this));
  List<AsyncCall> executableCalls = new ArrayList<>();
  boolean isRunning;
  synchronized (this) {
    // 从 readyAsyncCalls 中取出可执行的请求先存放到临时的 executableCalls 中,
    // 以及转移到 runningAsyncCalls 中
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall asyncCall = i.next();
      // 如果正在运行的请求达到了最大值,则先退出
      if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
      // 如果请求 asyncCall 对应的主机的连接数达到了最大值,则先跳过该请求
      if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
      i.remove();
      // 将 asyncCall 的 callsPerHost 自增 1,callsPerHost 的作用在上面讲过
      asyncCall.callsPerHost().incrementAndGet();
      executableCalls.add(asyncCall);
      runningAsyncCalls.add(asyncCall);
    }
    isRunning = runningCallsCount() > 0;
  }
  // 执行 executableCalls 中存放的请求
  for (int i = 0, size = executableCalls.size(); i < size; i++) {
    AsyncCall asyncCall = executableCalls.get(i);
    // 通过 executorService 线程池来执行 asyncCall 请求
    asyncCall.executeOn(executorService());
  }
  return isRunning;
}

可以看到,对于一个异步请求,会包装成 AsyncCall,然后通过 Dispatcher 中的线程池 executorService 来执行。

AsyncCall 继承自 NamedRunnableNamedRunnable 则实现了 Runnable 而在 NamedRunnable#run() 方法中,会执行 NamedRunnable#execute() 这一抽象方法。AsyncCall 则实现 execute() 方法。

AsyncCall # executeOn()

/**
 * Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up
 * if the executor has been shut down by reporting the call as failed.
 */
void executeOn(ExecutorService executorService) {
  ...
  // 通过线程池来执行当前 realCall,因为 realCall 本质上就是 Runnable
  // 这样,就切换到了子线程中,从而实现了异步
  executorService.execute(this);  
  ...
}

线程池 executorService 的默认实现在前面有说(即通过 Dispatcher # executorService() 方法)。

因此,executorService.execute(this) 实际上会触发 AsyncCallexecute() 方法(因为其父类 NamedRunnable#run() 的缘故)

@Override protected void execute() {
  boolean signalledCallback = false;
  // 用于监测超时的
  timeout.enter();
  try {
    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) {
    e = timeoutExit(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() 来实现请求的。

有关 getResponseWithInterceptorChain() 可以参见:okhttp 源码解析笔记整理——拦截器与请求的实现


另外,对于 readyAsyncCalls 也许会有一个疑问,那就是怎么保证该队列中的请求都能执行完呢?

这里可以追溯源码,根据前面的内容可以直到,Dispather # promoteAndExecute() 方法会推动 readyAsyncCalls 中的请求的执行(使其转移到 runningAsyncCalls 中)。

Dispather # promoteAndExecute() 方法会在多个地方被调用,其中就有在 Dispather # finished() 中被调用:

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

而该方法又有在 Dispather 的两个重载方法中被调用:

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

/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
  finished(runningSyncCalls, call);
}

而第一个重载方法会在 RealCall # executeOn()RealCall.Async # execute() 中被调用;

第二个重载方法会在 RealCall # execute() 被调用。

这样,就可以保证 Dispather # promoteAndExecute() 方法重复不断的被触发,最终保证 readyAsyncCalls 中的请求都被执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值