分析每个类的源码,从源码的角度解析类的功能组成及类与类之间的关系。
HttpUrl
这个类说简单也简单,就是把一个字符串url拆解出各部分。
注释里提出一个问题就是:为什么java里已经有了java.net.URL和java.net.URI,却还要再自定义一个HttpUrl呢,主要是因为这两个api的类都有问题。
首先,对java.net.URL:
下面这两个url是相等的,因为他们有相同的IP地址。
http://square.github.io/
http://google.github.io/
这是一个相当不好的设计,直接导致Url无法在Map和Set中使用。除此之外,这还会导致低效。因为这种相等性会导致Dns查询,而且还会出现错误。
对于java.net.URI:
又会出现另一种问题,下面的两个url应该是相等的,但是URI认为是两个不同的url
http://host:80/
http://host
而HttpUrl解决了上面两种问题。
HttpUrl也提供了向两个类的转换方法:
url():返回一个java.net.URL
/** Returns this URL as a {@link URL java.net.URL}. */
public URL url() {
try {
return new URL(url);
} catch (MalformedURLException e) {
throw new RuntimeException(e); // Unexpected!
}
}
uri():返回一个URI,相当于是一个HttpUrl的严格模式
public URI uri() {
String uri = newBuilder().reencodeForUri().toString();
try {
return new URI(uri);
} catch (URISyntaxException e) {
// Unlikely edge case: the URI has a forbidden character in the fragment. Strip it & retry.
try {
String stripped = uri.replaceAll("[\\u0000-\\u001F\\u007F-\\u009F\\p{javaWhitespace}]", "");
return URI.create(stripped);
} catch (Exception e1) {
throw new RuntimeException(e); // Unexpected!
}
}
}
上面HttpUrl提出了对url相等性的讨论,所以它肯定会重写equals()和hashCode()两个方法
equals():
@Override public boolean equals(@Nullable Object other) {
return other instanceof HttpUrl && ((HttpUrl) other).url.equals(url);
}
hashCode():就是直接取得成员变量url字符串的hashcode。也就是说,HttpUrl可以向字符串一样在Map和Set中使用
@Override public int hashCode() {
return url.hashCode();
}
现在,我们来看看url变量的来历。
url来自于其建造者Builder的toString()方法:this.url = builder.toString();
看一下Builder的toString()方法,出乎意料,builder类中并没有对应的url成员,它是直接根据其他各种参数拼装出来的一个url,设置给了HttpUrl的url字段。
以下代码,仅供欣赏。
@Override public String toString() {
StringBuilder result = new StringBuilder();
result.append(scheme);
result.append("://");
if (!encodedUsername.isEmpty() || !encodedPassword.isEmpty()) {
result.append(encodedUsername);
if (!encodedPassword.isEmpty()) {
result.append(':');
result.append(encodedPassword);
}
result.append('@');
}
if (host.indexOf(':') != -1) {
// Host is an IPv6 address.
result.append('[');
result.append(host);
result.append(']');
} else {
result.append(host);
}
int effectivePort = effectivePort();
if (effectivePort != defaultPort(scheme)) {
result.append(':');
result.append(effectivePort);
}
pathSegmentsToString(result, encodedPathSegments);
if (encodedQueryNamesAndValues != null) {
result.append('?');
namesAndValuesToQueryString(result, encodedQueryNamesAndValues);
}
if (encodedFragment != null) {
result.append('#');
result.append(encodedFragment);
}
return result.toString();
}
HttpUrl是面向 url 字符串的
Address
与源服务器的链接规范。
1、对于简单的链接,Address代表的就是服务器的主机名host和端口port。
2、如果需要代理,那Address还包括了代理proxy相关的信息。
3、对于安全的链接,Address还会包含SSL socket factory, hostname verifier, 和 certificate pinner.。
也就是说,Address是面向链接的。
成员变量
public final class Address {
final HttpUrl url;
final Dns dns;
final SocketFactory socketFactory;
final Authenticator proxyAuthenticator;
final List protocols;
final List connectionSpecs;
final ProxySelector proxySelector;
final @Nullable Proxy proxy;
final @Nullable SSLSocketFactory sslSocketFactory;
final @Nullable HostnameVerifier hostnameVerifier;
final @Nullable CertificatePinner certificatePinner;
都是一些参数配置类型的信息。
构造方法:
public Address(String uriHost, int uriPort, Dns dns, SocketFactory socketFactory,
@Nullable SSLSocketFactory sslSocketFactory, @Nullable HostnameVerifier hostnameVerifier,
@Nullable CertificatePinner certificatePinner, Authenticator proxyAuthenticator,
@Nullable Proxy proxy, List protocols, List connectionSpecs,
ProxySelector proxySelector) {
this.url = new HttpUrl.Builder()//创建成员HttpUrl
.scheme(sslSocketFactory != null ? "https" : "http") //可以看到,有ssl,就是https。没有就是http
.host(uriHost)
.port(uriPort)
.build();
if (dns == null) throw new NullPointerException("dns == null"); //必要信息,dns不能为空
this.dns = dns;
if (socketFactory == null) throw new NullPointerException("socketFactory == null");//必要信息,socketFactory 不能为空
this.socketFactory = socketFactory;
if (proxyAuthenticator == null) {
throw new NullPointerException("proxyAuthenticator == null");
}
this.proxyAuthenticator = proxyAuthenticator;
if (protocols == null) throw new NullPointerException("protocols == null");
this.protocols = Util.immutableList(protocols);
if (connectionSpecs == null) throw new NullPointerException("connectionSpecs == null");
this.connectionSpecs = Util.immutableList(connectionSpecs);
if (proxySelector == null) throw new NullPointerException("proxySelector == null");
this.proxySelector = proxySelector;
//以上是必要信息,均不能为空
this.proxy = proxy;
this.sslSocketFactory = sslSocketFactory;
this.hostnameVerifier = hostnameVerifier;
this.certificatePinner = certificatePinner;
}
equals(@Nullable Object other):两个Address怎样是相等的呢
@Override public boolean equals(@Nullable Object other) {
return other instanceof Address
&& url.equals(((Address) other).url)//首先要比较两个的url是否相等
&& equalsNonHost((Address) other);//最后比较address自身的一些属性是否相等
}
equalsNonHost(Address that):比较两个address自身的一些属性是否相等
除了个成员变量必须要equals之外,dns需要相等。另外,url的端口号port必须也要相等。
boolean equalsNonHost(Address that) {
return this.dns.equals(that.dns)//dns相等
&& this.proxyAuthenticator.equals(that.proxyAuthenticator)
&& this.protocols.equals(that.protocols)
&& this.connectionSpecs.equals(that.connectionSpecs)
&& this.proxySelector.equals(that.proxySelector)
&& equal(this.proxy, that.proxy)
&& equal(this.sslSocketFactory, that.sslSocketFactory)
&& equal(this.hostnameVerifier, that.hostnameVerifier)
&& equal(this.certificatePinner, that.certificatePinner)
&& this.url().port() == that.url().port();//url的端口号port必须也要相等。
}
hashCode():仅供欣赏
这一部分不懂了:
为啥result的初始值是 17 ?
又为啥每次要乘以 31 ?
因为是31 = 2*5 - 1,17 = 2*5 / 2 + 1 ?
@Override public int hashCode() {
int result = 17;
result = 31 * result + url.hashCode();
result = 31 * result + dns.hashCode();
result = 31 * result + proxyAuthenticator.hashCode();
result = 31 * result + protocols.hashCode();
result = 31 * result + connectionSpecs.hashCode();
result = 31 * result + proxySelector.hashCode();
result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
result = 31 * result + (sslSocketFactory != null ? sslSocketFactory.hashCode() : 0);
result = 31 * result + (hostnameVerifier != null ? hostnameVerifier.hashCode() : 0);
result = 31 * result + (certificatePinner != null ? certificatePinner.hashCode() : 0);
return result;
}
Address是面向链接的。
Dns
Dns是一个接口
为主机名解析IP地址的域名服务。
//大部分应用都是使用默认的dns,提供自定义的dns可以处理ipV6, IPv4, 或者某些具体的ip
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通过调用InetAddress的getAllByName()来要求底层的操作系统查找IP地址。
//大部分自定义的dns需要委托给这个实例。
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;
}
}
};
/**
* Returns the IP addresses of {@code hostname}, in the order they will be attempted by OkHttp. If
* a connection to an address fails, OkHttp will retry the connection with the next address until
* either a connection is made, the set of IP addresses is exhausted, or a limit is exceeded.
*/
//根据一个主机名,返回对应的IP地址,多个IP地址会按照Okhttp要求的顺序排序。
//如果到一个Address的链接失败了,okhttp会取下一个 address 重新尝试链接,直到链接成功,或者address都用完了,或者超时了。
List lookup(String hostname) throws UnknownHostException;
}
Dns是面向Address。
Proxy
一个proxy代表的是一个代理设置,通常是一个type类型,和一个socket地址。
一个proxy是一个不可变的对象
成员变量:
public class Proxy {
/**
* Represents the proxy type.
*
* @since 1.5
*/
//枚举 Type代表的是代理的类型,
//一共三种:
public enum Type {
/**
* Represents a direct connection, or the absence of a proxy.
*/
//表示直接连接,或没有代理。
DIRECT,
/**
* Represents proxy for high level protocols such as HTTP or FTP.
*/
//表示高级协议(如HTTP或FTP)的代理。
HTTP,
/**
* Represents a SOCKS (V4 or V5) proxy.
*/
//表示SOCKS(V4或V5)代理。
SOCKS
};
private Type type;//自己的类型,
private SocketAddress sa;//socket的地址
/**
* A proxy setting that represents a {@code DIRECT} connection,
* basically telling the protocol handler not to use any proxying.
* Used, for instance, to create sockets bypassing any other global
* proxy settings (like SOCKS):
* * {@code Socket s = new Socket(Proxy.NO_PROXY);}
*
*/
public final static Proxy NO_PROXY = new Proxy();
构造方法:
默认无参构造的是一个直连无代理的链接。
当要指定代理type和socketAddress的时候,这两者需要匹配。
// Creates the proxy that represents a {@code DIRECT} connection.
private Proxy() {
type = Type.DIRECT;
sa = null;
}
/**
* Creates an entry representing a PROXY connection.
* Certain combinations are illegal. For instance, for types Http, and
* Socks, a SocketAddress must be provided.
* * Use the {@code Proxy.NO_PROXY} constant
* for representing a direct connection.
*
* @param type the {@code Type} of the proxy
* @param sa the {@code SocketAddress} for that proxy
* @throws IllegalArgumentException when the type and the address are
* incompatible
*/
public Proxy(Type type, SocketAddress sa) {
if ((type == Type.DIRECT) || !(sa instanceof InetSocketAddress))
throw new IllegalArgumentException("type " + type + " is not compatible with address " + sa);
this.type = type;
this.sa = sa;
}
equals():Proxy是如何相等的
必须type和socketAddress两者都相等才行
是允许socketAddress为空值的情况的
* Compares this object against the specified object.
* The result is {@code true} if and only if the argument is
* not {@code null} and it represents the same proxy as
* this object.
* * Two instances of {@code Proxy} represent the same
* address if both the SocketAddresses and type are equal.
*
* @param obj the object to compare against.
* @return {@code true} if the objects are the same;
* {@code false} otherwise.
* @see java.net.InetSocketAddress#equals(java.lang.Object)
*/
public final boolean equals(Object obj) {
if (obj == null || !(obj instanceof Proxy))
return false;
Proxy p = (Proxy) obj;
if (p.type() == type()) {
if (address() == null) {
return (p.address() == null);
} else
return address().equals(p.address());
}
return false;
}
hashCode():仅供欣赏
/**
* Returns a hashcode for this Proxy.
*
* @return a hash code value for this Proxy.
*/
public final int hashCode() {
if (address() == null)
return type().hashCode();
return type().hashCode() + address().hashCode();
}
Proxy也是面向Address的,是为Address服务的
ProxySelector
ProxySelector是一个抽象类,实现的选择器应该注册到ProxySelector里面。
ProxySelector自身提供了一个默认的ProxySelector实例:DefaultProxySelector作为选择器。
类注释:
//当通过一个URL链接到网络资源的时候,通过ProxySelector来选择一个代理服务器。
//一个实际的proxySelector是这个ProxySelector的子类。并且通过调用setDefault方法进行注册。
//当然,也可以调用getDefault方法获得注册的代理选择器。
//当一个代理选择器注册后,UrlConnection的子类应该为每一个URL请求都调用select方法,
//这样代理选择器就会确定到底是要建立一个直连的还是代理的链接
//select方法返回的是一个迭代器
//如果一个链接无法建立到代理服务器的链接,那调用者需要调用选择器的connectFailed方法
public abstract class ProxySelector {
/**
* The system wide proxy selector that selects the proxy server to
* use, if any, when connecting to a remote object referenced by
* an URL.
*
* @see #setDefault(ProxySelector)
*/
private static ProxySelector theProxySelector;
static {
try {
Class c = Class.forName("sun.net.spi.DefaultProxySelector");
if (c != null && ProxySelector.class.isAssignableFrom(c)) {
theProxySelector = (ProxySelector) c.newInstance();//通过动态加载类的方式创建
}
} catch (Exception e) {
theProxySelector = null;
}
}
select(URI uri):如何选的我不知道,我想知道返回的是什么
public abstract List select(URI uri);
总之就是基于一切可基于的参数,选择出一个proxy代理列表,当没有代理可用的时候,这个方法也会返回一个列表,只不过里面只有一个元素,那就是代表直连的Proxy.NO_PROXY
很显然, ProxySelector面向Proxy
需要attention一下,ProxySelector和Proxy都是Java自己的api,它们不是Okhttp滴。
Route
路线。
链接的建立需要两部分一个是IP address,一个是代理proxy。IP是链接的目的地,Proxy表示如何到达目的地。
而一个Route,则表示决策后的一条可选的路线:通过方式M,到达目的地N。
成员变量:
public final class Route {
final Address address;
final Proxy proxy;
final InetSocketAddress inetSocketAddress;
根据注释的推测,本以为会有两个成员,没想到多出来了一个InetSocketAddress。
关于InetSocketAddress:
InetSocketAddress是SocketAddress的实现子类。
此类实现 IP 套接字地址(IP 地址 + 端口号),不依赖任何协议。
在使用Socket来连接服务器时最简单的方式就是直接使用IP和端口,但Socket类中的connect方法并未提供这种方式,而是使用SocketAddress类来向connect方法传递服务器的IP和端口。
SocketAddress只是个抽象类,它除了有一个默认的构造方法外,其它的方法都是abstract的,因此,我们必须使用SocketAddress的子类来建立SocketAddress对象,也就是唯一的子类InetSocketAddress
构造方法:
唯一的调用点就RouteSelector中的next方法。
public Route(Address address, Proxy proxy, InetSocketAddress inetSocketAddress) {
if (address == null) {
throw new NullPointerException("address == null");
}
if (proxy == null) {
throw new NullPointerException("proxy == null");
}
if (inetSocketAddress == null) {
throw new NullPointerException("inetSocketAddress == null");
}
//这三个都不能是空的
this.address = address;
this.proxy = proxy;
this.inetSocketAddress = inetSocketAddress;
}
requiresTunnel():请求隧道
此路由需要通过HTTP代理对HTTPS进行隧道。
/**
* Returns true if this route tunnels HTTPS through an HTTP proxy. See RFC 2817, Section 5.2.
*/
public boolean requiresTunnel() {
//条件就是 是Https链接,但是代理类型是Proxy.Type.HTTP
return address.sslSocketFactory != null && proxy.type() == Proxy.Type.HTTP;
}
equals():相等
三个成员变量同时相等
@Override public boolean equals(@Nullable Object other) {
return other instanceof Route
&& ((Route) other).address.equals(address)
&& ((Route) other).proxy.equals(proxy)
&& ((Route) other).inetSocketAddress.equals(inetSocketAddress);
}
hashCode():仅供欣赏
@Override public int hashCode() {
int result = 17;
result = 31 * result + address.hashCode();
result = 31 * result + proxy.hashCode();
result = 31 * result + inetSocketAddress.hashCode();
return result;
}
Route是面向Address和Proxy两个的。
RouteSelector
唯一创建的地方就是StreamAllocation中:
this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
成员变量
//选择出一串链接到源服务器的route,每个链接都需要选择一个代理服务器,IP,TLS模式。
//链接有时还可以复用
public final class RouteSelector {
private final Address address;
private final RouteDatabase routeDatabase;//无用route的黑名单
private final Call call;//监听生命周期用的
private final EventListener eventListener;//监听生命周期用的
/* State for negotiating the next proxy to use. */
private List proxies = Collections.emptyList();//协商下一个要使用的代理,注意这是代理Proxy
private int nextProxyIndex;//下一个代理Proxy的下标
/* State for negotiating the next socket address to use. */
private List inetSocketAddresses = Collections.emptyList();//协商下一个要是用的socket地址
/* State for negotiating failed routes */
private final List postponedRoutes = new ArrayList<>();//失败的route列表
构造方法:
public RouteSelector(Address address, RouteDatabase routeDatabase, Call call,
EventListener eventListener) {
this.address = address;
this.routeDatabase = routeDatabase;
this.call = call;
this.eventListener = eventListener;
//赋值完成后,立即准备好代理服务器
resetNextProxy(address.url(), address.proxy());
}
resetNextProxy(HttpUrl url, Proxy proxy):准备好选择器
private void resetNextProxy(HttpUrl url, Proxy proxy) {
//参数url和proxy均来自address
if (proxy != null) {
// If the user specifies a proxy, try that and only that.
proxies = Collections.singletonList(proxy);//如果指定了一个代理,则放在列表里。
} else {
// Try each of the ProxySelector choices until one connection succeeds.
//如果没有代理
List proxiesOrNull = address.proxySelector().select(url.uri());//则通过代理选择器选出可用的代理
proxies = proxiesOrNull != null && !proxiesOrNull.isEmpty()
? Util.immutableList(proxiesOrNull)
: Util.immutableList(Proxy.NO_PROXY);
}
nextProxyIndex = 0;
}
所以最后可用的代理列表有三种:
1、address直接提供的proxy,这时候列表只有这一项。
2、address未提供时,通过address的代理选择器得到了,这时候是整个代理选择器提供的proxy列表
3、address未提供时,通过address的代理选择器什么也没得到,这时候列表是一个Proxy.NO_PROXY
hasNext():是否还有可选择的route
/**
* Returns true if there's another set of routes to attempt. Every address has at least one route.
*/
//注释里提到一个前提:每一个address都至少有一个route
public boolean hasNext() {
return hasNextProxy() || !postponedRoutes.isEmpty();
}
所以还有route的条件就是要么还有可用的proxy,要么postponedRoutes列表不为空
hasNextProxy():是否还有可尝试的代理proxy
/** Returns true if there's another proxy to try. */
private boolean hasNextProxy() {
return nextProxyIndex < proxies.size();//没查询到列表尾部就算还有
}
next():查找下一波可用的route
public Selection next() throws IOException {
if (!hasNext()) {
throw new NoSuchElementException();
}
// Compute the next set of routes to attempt.
List routes = new ArrayList<>();//装着可尝试的route的集合
while (hasNextProxy()) {//遍历代理列表
// Postponed routes are always tried last. For example, if we have 2 proxies and all the
// routes for proxy1 should be postponed, we'll move to proxy2. Only after we've exhausted
// all the good routes will we attempt the postponed routes.
//推迟的路线总是最后一次尝试。之所以叫推迟,是因为这些route都是最近失败的。如果proxy1的route都是推迟的,那就优先处理proxy,最后还是不行的话,再回到proxy处理
Proxy proxy = nextProxy();//取下一个代理,
for (int i = 0, size = inetSocketAddresses.size(); i < size; i++) {
Route route = new Route(address, proxy, inetSocketAddresses.get(i));//生成一个route
if (routeDatabase.shouldPostpone(route)) {//如果route最近失败了
postponedRoutes.add(route);//先将route加入到失败列表中
} else {
routes.add(route);//一个正常的route,加入到routes列表中
}
}
if (!routes.isEmpty()) {//针对上面刚刚取到的那个proxy,如果得到可用的route了,则退出while,
break;
}
}
if (routes.isEmpty()) {//如果没有可用的route,那就把上面循环时取到哪些之前失败过的routeadd进来
// We've exhausted all Proxies so fallback to the postponed routes.
routes.addAll(postponedRoutes);
postponedRoutes.clear();
}
//将本次选出的route封装进内部类Selection中。
return new Selection(routes);
}
我们知道Route是面向address和proxy的。所以一个route的创建必须要有这两者。
先得到一个代理proxy,然后循环inetSocketAddresses,根据proxy和address和inetSocketAddresses.get(i)组成一个Route。
这时候,会对新产生的Route状态进行判断,如果这个Route之前失败过,那就把它先放在失败Route列表postponedRoutes种。否则就放在正常的routes列表中。
当循环完一次postponedRoutes后,判断是否有正常的Route,如果有,则退出整个while循环。
来到vwhile外面,表示获取Route的操作已结束。这时候判断是否有可用route,如果没有, 那就把之前得到的那些失败过的route再拿来试一遍。
最后生成的可用routes被封装成内部类Selection。
nextProxy():获取下一个代理
/** Returns the next proxy to try. May be PROXY.NO_PROXY but never null. */
private Proxy nextProxy() throws IOException {
if (!hasNextProxy()) {
throw new SocketException("No route to " + address.url().host()
+ "; exhausted proxy configurations: " + proxies);//没有代理可用,直接无法创建了链接。抛出Socket异常
}
Proxy result = proxies.get(nextProxyIndex++);//从列表中取下一个
resetNextInetSocketAddress(result);//为当前代理或主机准备要尝试的套接字地址。
return result;
}
resetNextInetSocketAddress(Proxy proxy):为当前代理或主机准备要尝试的套接字地址。
/** Prepares the socket addresses to attempt for the current proxy or host. */
private void resetNextInetSocketAddress(Proxy proxy) throws IOException {
// Clear the addresses. Necessary if getAllByName() below throws!
inetSocketAddresses = new ArrayList<>();
String socketHost;//主机名
int socketPort;端口
if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
//如果proxy是直接或者socks,取address的主机和端口
socketHost = address.url().host();
socketPort = address.url().port();
} else {
//如果代理类型是http
//取代理的address中的主机和端口
SocketAddress proxyAddress = proxy.address();
if (!(proxyAddress instanceof InetSocketAddress)) {
throw new IllegalArgumentException(
"Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
}
InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
socketHost = getHostString(proxySocketAddress);
socketPort = proxySocketAddress.getPort();
}
if (socketPort < 1 || socketPort > 65535) {
throw new SocketException("No route to " + socketHost + ":" + socketPort
+ "; port is out of range");
}
if (proxy.type() == Proxy.Type.SOCKS) {
inetSocketAddresses.add(InetSocketAddress.createUnresolved(socketHost, socketPort));
} else {
eventListener.dnsStart(call, socketHost);
// Try each address for best behavior in mixed IPv4/IPv6 environments.
List addresses = address.dns().lookup(socketHost);//启动dns
if (addresses.isEmpty()) {
throw new UnknownHostException(address.dns() + " returned no addresses for " + socketHost);
}
eventListener.dnsEnd(call, socketHost, addresses);
for (int i = 0, size = addresses.size(); i < size; i++) {
InetAddress inetAddress = addresses.get(i);
inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
}
}
}
根据代理的类型为,取不同的主机名和端口号,生成下一步可用的inetSocketAddresses。这个过程中涉及到了DNS
List addresses = address.dns().lookup(socketHost);//启动dns
内部类:Selection : 就是一个可用route的列表
封装了对routes列表的取值,不用每一个用到的地方都自己去循环取值了。
/** A set of selected Routes. */
public static final class Selection {
private final List routes;
private int nextRouteIndex = 0;
Selection(List routes) {
this.routes = routes;
}
public boolean hasNext() {
return nextRouteIndex < routes.size();
}
public Route next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return routes.get(nextRouteIndex++);
}
public List getAll() {
return new ArrayList<>(routes);
}
}
RouteSelector 是面向Route的
RouteDatabase
一个黑名单
/**
* A blacklist of failed routes to avoid when creating a new connection to a target address. This is
* used so that OkHttp can learn from its mistakes: if there was a failure attempting to connect to
* a specific IP address or proxy server, that failure is remembered and alternate routes are
* preferred.
*/
public final class RouteDatabase {
private final Set failedRoutes = new LinkedHashSet<>();
/** Records a failure connecting to {@code failedRoute}. */
public synchronized void failed(Route failedRoute) {
failedRoutes.add(failedRoute);
}
/** Records success connecting to {@code route}. */
public synchronized void connected(Route route) {
failedRoutes.remove(route);
}
/** Returns true if {@code route} has failed recently and should be avoided. */
public synchronized boolean shouldPostpone(Route route) {
return failedRoutes.contains(route);
}
}