简析框架使用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可算是大显身手
在这里,深深的影响着最终链接的建立。