okhttp3实例源码浅析(1)-Dispatcher

先从最简单的例子入手分析

OkHttpClient client = new OkHttpClient.Builder().build();
        Request request = new Request.Builder()
                .url(api).get().build();
        client.newCall(request).enqueue(new Callback() {
            @Override public void onFailure(Call call, IOException e) {
            }
            @Override public void onResponse(Call call, Response response) {
            }
        });

1.首先构建了OkHttpClient对象(实际开发中会使用单例模式来构建获取OkHttpClient);
2.接着构建Request请求对象,这里封装了最基本的接口地址和请求方式;
3.最后传入回调方法并发起接口请求。

下面逐步分析每个步骤

一.构建OkHttpClient

···
//拦截器
final List<Interceptor> interceptors = new ArrayList<>();
final List<Interceptor> networkInterceptors = new ArrayList<>();
···
public Builder() {
      dispatcher = new Dispatcher(); //请求任务调度器
      protocols = DEFAULT_PROTOCOLS; //h2、http/1.1协议
      connectionSpecs = DEFAULT_CONNECTION_SPECS; //不同版本TLS相关配置
      eventListenerFactory = EventListener.factory(EventListener.NONE); //okhttp发起请求过程中会回调的监听器
      proxySelector = ProxySelector.getDefault(); //代理服务器选择器
      cookieJar = CookieJar.NO_COOKIES; //管理HTTP cookies的工具接口
      socketFactory = SocketFactory.getDefault(); //创建socket,同时做一些扩展
      hostnameVerifier = OkHostnameVerifier.INSTANCE; //校验证书的工具类
      certificatePinner = CertificatePinner.DEFAULT; //管理信任证书的工具类
      proxyAuthenticator = Authenticator.NONE; //代理证书
      authenticator = Authenticator.NONE; //证书
      connectionPool = new ConnectionPool(); //连接池复用
      dns = Dns.SYSTEM; //DNS解析工具接口,对InetAddress.getAllByName(hostname)做了一次封装
      followSslRedirects = true; //允许重定向
      followRedirects = true; //允许重定向
      retryOnConnectionFailure = true; //允许失败重试
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }

以上是OkHttpClient构建时的默认配置,从这里就可以看出okhttp做了很多封装和优化,包括请求任务队列、协议兼容、SSL、连接池复用、重定向、重连机制、拦截器机制等。

二.创建Request

通过url方法将传入的接口地址拆解,用HttpUrl对象保存,拆解的组成部分有scheme、host、path、query等

public Builder url(String url) {
      if (url == null) throw new NullPointerException("url == null");

      // Silently replace web socket URLs with HTTP URLs.
      if (url.regionMatches(true, 0, "ws:", 0, 3)) {
        url = "http:" + url.substring(3);
      } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
        url = "https:" + url.substring(4);
      }
      // 解析url
      HttpUrl parsed = HttpUrl.parse(url);
      if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
      return url(parsed);
}

设置GET请求方式,同理还有POST方法

public Builder get() {
      return method("GET", null);
}
public Builder post(RequestBody body) {
      return method("POST", body);
}
public Builder method(String method, @Nullable RequestBody body) {
      if (method == null) throw new NullPointerException("method == null");
      if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
      if (body != null && !HttpMethod.permitsRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must not have a request body.");
      }
      if (body == null && HttpMethod.requiresRequestBody(method)) {
        throw new IllegalArgumentException("method " + method + " must have a request body.");
      }
      this.method = method; // 赋值请求方式
      this.body = body; // 保存请求体(get请求没有请求体)
      return this;
}

这里使用最基本的配置创建Request对象,其中保存了url、headers、method、body等参数。还有一个tag参数,取消请求的时候会用到该参数。

三.发起请求

1.首先调用OkHttpClient的newCall方法创建RealCall对象,后续由该对象调用相关请求方法
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client; // 保存okHttpClient引用
    this.originalRequest = originalRequest; // 保存request
    this.forWebSocket = forWebSocket; // forWebSocket默认为false
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket); // 创建重试和重定向拦截器
}

RealCall实现了Call接口的方法:

  • Request request(); 返回保存的Request对象
  • Response execute() throws IOException; 执行同步请求
  • void enqueue(Callback responseCallback); 加入任务队列执行异步请求
  • void cancel(); 取消未完成的请求
  • boolean isExecuted(); 判断是否已执行execute/enqueue,一个call只能执行发起一次
  • boolean isCanceled(); 判断是否已取消,即知否执行了cancel
  • Call clone(); 复制一个一样的call,可以执行execute/enqueue无论原来的call是否已执行
2.创建完RealCall后,调用enqueue方法将请求加入队列
@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback)); // 加入到任务队列,通过Dispatcher对象分发事件
}

这里先创建AsyncCall将responseCallback包装进去,AsyncCall继承自NamedRunnable,在NamedRunnable的run方法中又调用了AsyncCall的execute方法。

加入任务的就是AsyncCall对象,Dispatcher内部通过三个双端队列来保存请求:

Deque<RealCall> runningSyncCalls; //保存正在执行的同步请求的RealCall
Deque<AsyncCall> readyAsyncCalls; //保存异步请求的AsyncCall(等待执行)
Deque<AsyncCall> runningAsyncCalls; //保存异步请求的AsyncCall(正在执行),该队列限制了总请求数不能超过64个且每个host下的请求数不超过5个
synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      //未超出容量,加入running队列,用线程池取线程去执行
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      //否则加入ready队列
      readyAsyncCalls.add(call);
    }
}

如果执行,则调用AsyncCall.execute()

@Override protected void execute() {
      boolean signalledCallback = false;
      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) {
        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); // 请求完成,从队列清除任务,并触发后续操作
      }
    }
}
3.请求完成调用Dispatcher的finished方法
/** 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(); // 异步请求完成的为true
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run(); // 当前在请求中的任务为0(包括异步和同步),且设置了idleCallback(通过setIdleCallback方法设置),则触发idleCallback
    }
}

此时为异步请求,会调用promoteCalls方法

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity. 已经达到最大任务数
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. 没有待请求的任务

    // 遍历待请求任务队列
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      // 如果满足同个host下的请求任务小于5个,则将它从readyAsyncCalls移至runningAsyncCalls,然后执行请求任务
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      // 再对runningAsyncCalls做一次检查
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
}

以上是okhttp发起网络请求的大致流程,先简单分析到这里,改天继续研究getResponseWithInterceptorChain()中的拦截器机制的实现。

猜你喜欢

转载自blog.csdn.net/dehang0/article/details/80460919