OkHttp源码解析——分发器

OkHttp使用流程

首先看一下OkHttp大概的使用流程:
1.创建OkHttpClient对象,使用默认配置或用建造者模式进行自己的配置

2.client作为网络请求器,把请求Request交给他,得到随时可以工作的Call对象(Call是接口,实现类是RealCall)

3.Call的execute或enqueue

4.给到分发器Dispatcher。

分发器:内部维护队列与线程池,完成请求调配。发送一个http请求,再发一个要等待上一个请求完成第二个才能执行吗?所以要用到并发,用到线程池。

5.再给到拦截器Interceptors。

拦截器:五大默认拦截器完成整个请求过程。

6.通过后把请求给服务器得到Response,再给拦截器再给用户。

分发器解析

我们先主要来看分发器
Call是什么,拿call.enqueue()来看。newCall()里面新建了RealCall对象,然后是RealCall.enqueue()。

enqueue需要一个Callback,可以告诉我们请求有了响应或是失败

每次请求都会新建一个Call对象,每个Call只能执行一次,如果再次执行,会抛出异常

void enqueue(Callback responseCallback, boolean forWebSocket) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }

然后得到client的分发器,当然分发器可自己定义,其实默认的足够。然后新建AsyncCall交给dispatcher,去执行enqueue()方法。

异步有两个队列,一个Ready,一个Running队列(都是双端队列ArrayQueue,但目前没有用到双端)。等一会执行或马上执行

问题一: 如何决定将请求放入ready队列(readyAsyncCalls)还是running队列(runningAsyncCalls)?

Dispatcher的enqueue()方法。如果运行队列中任务数(正在执行的请求)小于64 ,并且正在执行的队列里面与现在的请求的Host相同的有5个以下(也就是相同Host的请求不能超过5个),放入running队列,否则放入ready队列。(前者客户端压力,后者服务器压力,并且可以通过setMaxRequests()与setMaxRequestsPerHost()方法去修改)

private int maxRequests = 64;

private int maxRequestsPerHost = 5;

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);//线程池马上跑任务,ThreadPool
    } else {
      readyAsyncCalls.add(call);
    }
  }

问题二: ready队列里的何时执行?

任务执行完,AsyncCall中的execute()中的finally->分发器的finished():将call从running队列中移除,考虑从ready队列中获取任务放入到running队列中(promoteCalls())

synchronized void finished(AsyncCall call) {
  if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
  promoteCalls();
}
private void promoteCalls() {
    //若队列任务大于等于64,退!若ready队列空,退!
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
    //遍历,还有一个限制!对同一Host的请求要小于5。然后放满了就不放了
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

同步请求的话比较简单,没有什么限制。dispatcher的executed方法。

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

RealCall execute()的finally中finished()方法

void finished(RealCall call) {
  finished(runningSyncCalls, call);
}

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

同步请求队列是runningSyncCalls。异步请求队列有runningAsyncCalls和readyAsyncCalls。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听♞海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值