OkHttp3地址解析之一个url点燃的繁华世界(1/2)

981 阅读3分钟

简析框架使用url地址的流程,及涉及到的类都有哪些。

梗概:

Okhttp是如何根据一个普通的url字符串建立连接的。

先来个总结图:

HttpUrl

面向 url 字符串

将url封装成对象

Address

面向底层链接

提供了一个链接规范,屏蔽了底层的具体链接方式,一个address就是一个目的地地址

Dns

面向 Address

提供可用的address

Proxy

面向Address

为Address服务,提供一种到达address的方式,直连,代理?

ProxySelector

面向Proxy

选择出可用的proxy

Route

面向Address和Proxy两个的。

将一个可达的address和通过的proxy方式封装起来,代表了通过方式M到达目的地N。

RouteSelector

面向Route

选择出可用的Route

RouteDatabase

面壁思过

一个黑名单,总之要说它面向谁,面向Route

发起一次网络请求,最主要的部分就是url。比如一个url是https:www.baidu.com。

在我们的角度,一个url就是一个字符串,但是从okhttp的角度来看,url不仅不是一个字符串,也不是一个对象,而是一堆对象的协作体。我们提供的一个单纯的url字符串https:www.baidu.com,被转换成HttpUrl,赋予了代理、路由,代理选择器、路由选择器、dns。

小清新url字符串是如何进入Okhttp3名媛圈的呢

首先url进入Request,在Request中:url被转换成HttpUrl

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

  return url(HttpUrl.get(url));
}

HttpUrl使用builder模式,生成了自己的各种信息。

HttpUrl(Builder builder) {
  this.scheme = builder.scheme;
  this.username = percentDecode(builder.encodedUsername, false);
  this.password = percentDecode(builder.encodedPassword, false);
  this.host = builder.host;
  this.port = builder.effectivePort();
  this.pathSegments = percentDecode(builder.encodedPathSegments, false);
  this.queryNamesAndValues = builder.encodedQueryNamesAndValues != null
      ? percentDecode(builder.encodedQueryNamesAndValues, true)
      : null;
  this.fragment = builder.encodedFragment != null
      ? percentDecode(builder.encodedFragment, false)
      : null;
  this.url = builder.toString();
}

然后代码来到了第一个拦截器RetryAndFollowUpInterceptor,Address出现

在这里,生成了Address,并被赋值给了StreamAllocation的成员变量

StreamAllocation streamAllocation = new StreamAllocation(client.connectionPool(),
    createAddress(request.url()), call, eventListener, callStackTrace);

HttpUrl和Client提供的参数一起组合成了Address对象。

private Address createAddress(HttpUrl url) {
  SSLSocketFactory sslSocketFactory = null;
  HostnameVerifier hostnameVerifier = null;
  CertificatePinner certificatePinner = null;
  if (url.isHttps()) {
    sslSocketFactory = client.sslSocketFactory();
    hostnameVerifier = client.hostnameVerifier();
    certificatePinner = client.certificatePinner();
  }

  return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
      sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
      client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
}

Dns,Proxy,ProxySelector均在这里出现了。它们全部来自Client提供的配置。

这几个全部被塞进了Address,dns的取值来自CLient提供的配置,而这个配置是默认的,来自

dns = Dns.SYSTEM;

dns = Dns.SYSTEM;



public interface Dns {
  /**
   * A DNS that uses {@link InetAddress#getAllByName} to ask the underlying operating system to
   * lookup IP addresses. Most custom {@link Dns} implementations should delegate to this instance.
   */
  Dns SYSTEM = new Dns() {
    @Override public List lookup(String hostname) throws UnknownHostException {
      if (hostname == null) throw new UnknownHostException("hostname == null");
      try {
        return Arrays.asList(InetAddress.getAllByName(hostname));
      } catch (NullPointerException e) {
        UnknownHostException unknownHostException =
            new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
        unknownHostException.initCause(e);
        throw unknownHostException;
      }
    }
  };

接下来代码继续进入StreamAllocation,RouterSelector出现

根据address创建了RouterSelector

public StreamAllocation(ConnectionPool connectionPool, Address address, Call call,
    EventListener eventListener, Object callStackTrace) {
  this.connectionPool = connectionPool;
  this.address = address;
  this.call = call;
  this.eventListener = eventListener;
  this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
  this.callStackTrace = callStackTrace;
}

很显然,RouterSelector是用来选择出Route的,它一选选一堆,这一堆又放在routeSelection中,routeSelection最终得到能用的Route

private RouteSelector.Selection routeSelection;
private Route route;

Route皆诞生于此。

而重点是,new RouteSelector(),在这个选择器类的内部,开启了基于Route的繁华世界。

再跟着代码,StreamAllocation创建了RealConnection,Route也跟着来到了RealConnection

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
    int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
 。。。
    if (!foundPooledConnection) {
      if (selectedRoute == null) {
        selectedRoute = routeSelection.next();
      }

      // Create a connection and assign it to this allocation immediately. This makes it possible
      // for an asynchronous cancel() to interrupt the handshake we're about to do.
      route = selectedRoute;
      refusedStreamCount = 0;
      result = new RealConnection(connectionPool, selectedRoute);
      acquire(result, false);
    }
  }
。。。
  routeDatabase().connected(result.route());
。。。
}

进入RealConnection,route可算是大显身手

在这里,深深的影响着最终链接的建立。