OkHttp的整个的初始化采用了一个Builder的形式来建造,对设计模式不太了解的同学可以去看
https://github.com/mirsfang/ExamplesOfDesignPatterns
接下来进入正题
OkHttpClient的初始化
OkHttpClient在代码中是这样的
OkHttpClient okHttpClient=new OkHttpClient.Builder().build();
OkHttpClient 是一个Call 的工厂,可以发送http请求和读取他的相响应,通常OkHttpClient官方建议是把他共性,个人理解就是整个App或者client只有一个,不然的话每个client都拥有自己的连接池和线程池,每个client去创建连接池和线程池,这些资源都会被浪费掉的。
我们跟着上面的代码来去分析OkHttpClient
OkHttpClient通过Builder的build去构架的OkHttpClient,所以我们先看Builder类和Builder的build方法
首先是Builder的build方法:
public OkHttpClient build() {
return new OkHttpClient(this);
}
这就尴尬了,,我们来看Builder的构建方法
public Builder() {
dispatcher = new Dispatcher();//调度器 负责执行异步请求时的策略。
protocols = DEFAULT_PROTOCOLS; //协议,包含http1.1和http2
connectionSpecs = DEFAULT_CONNECTION_SPECS;//链接的规格(HTTPS的TSL规格)
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();//代理服务器
cookieJar = CookieJar.NO_COOKIES;//cookie管理
socketFactory = SocketFactory.getDefault();//sokcet工厂
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();//连接池
dns = Dns.SYSTEM; //Dns
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
我们从上面可以看到 Builder 主要是构建了基础的默认参数,上面注释了一些比较重要的参数,我们再来看OkHttpClient的构造函数
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
//证书信任管理类
X509TrustManager trustManager = systemDefaultTrustManager();
this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
}
从上面来看基本上整个OkHttpClient的值都是来自于Builder来实现的,具体的参数等等用到的时候再说
Request的请求初始化过程
Request的构建和OkHttpClient的构建基本差不多,但是Url不能为空 为空的话会抛出 IllegalStateException 的异常
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
一样的套路 看Builder的构建函数
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
默认是get请求,初始化了Headrs 也是利用builder的形式,里面是一个初始长度为20的List
HttpUrl url; //url
String method; //方法
Headers.Builder headers;//header的构建
RequestBody body;//请求体
Object tag;//tag
略微一看没啥大毛病,都是请求该有的东西,但是这个tag是干啥的,看下他的方法注释上面写道(以下带翻译)
/**
* Attaches {@code tag} to the request. It can be used later to cancel the request. If the tag
* is unspecified or null, the request is canceled by using the request itself as the tag.
*
* 将{@code tag}附加到请求。 稍后可以使用它来取消请求。 如果标签未指定或为空,则通过使用请求本身作为标签来取消该请求。
*
*/
public Builder tag(Object tag) {
this.tag = tag;
return this;
}
就是设置一个标志,能用这个标志去取消掉请求,如果没有指定也没关系,为空的话就用本身的Reques,相关代码在这里
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;//就是这里
}
method
在builder里面有个中各种各样的请求的方法(get post head delete 等)进去看代码发现都是调用了Request.Builder的method方法 如下:
public Builder head() {
return method("HEAD", null);
}
public Builder post(RequestBody body) {
return method("POST", body);
}
public Builder delete(RequestBody body) {
return method("DELETE", body);
}
public Builder delete() {
return delete(Util.EMPTY_REQUEST);
}
public Builder put(RequestBody body) {
return method("PUT", body);
}
public Builder patch(RequestBody body) {
return method("PATCH", body);
}
实现的方法如下
public Builder method(String method, 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;
return this;
}
主要也是判断了各种请求下的规则判断
head
在平常请求中header也是经常会用到的,在okhttp中header的构建也是在request里面的 相关的方法有
Builder header(String name, String value)
Builder addHeader(String name, String value)
Builder removeHeader(String name)
Builder headers(Headers headers)
追根溯源都是 Headers.Builder headers; 的操作,他和上面的一样,都说通Builder去构架的,所以我们先直接看他的Builder
public static final class Builder {
final List namesAndValues = new ArrayList<>(20);
/**
* Add a header line without any validation. Only appropriate for headers from the remote peer
* or cache.
*添加标题行而不进行任何验证。仅适用于远程对等体或缓存的头文件。
*/
Builder addLenient(String line) {
int index = line.indexOf(":", 1);
if (index != -1) {
return addLenient(line.substring(0, index), line.substring(index + 1));
} else if (line.startsWith(":")) {
// Work around empty header names and header names that start with a
// colon (created by old broken SPDY versions of the response cache).
return addLenient("", line.substring(1)); // Empty header name.
} else {
return addLenient("", line); // No header name.
}
}
/**
* Add an header line containing a field name, a literal colon, and a value.
* 添加一个包含字段名称,文字冒号和值的标题行。
*/
public Builder add(String line) {
int index = line.indexOf(":");
if (index == -1) {
throw new IllegalArgumentException("Unexpected header: " + line);
}
return add(line.substring(0, index).trim(), line.substring(index + 1));
}
/** Add a field with the specified value.
* 添加具有指定值的字段
*/
public Builder add(String name, String value) {
checkNameAndValue(name, value);
return addLenient(name, value);
}
/**
* Add a field with the specified value without any validation. Only appropriate for headers
* from the remote peer or cache.
*
* 添加具有指定值的字段,而不进行任何验证。仅适用于远程对等体或缓存的头文件。
*/
Builder addLenient(String name, String value) {
namesAndValues.add(name);
namesAndValues.add(value.trim());
return this;
}
public Builder removeAll(String name) {
for (int i = 0; i < namesAndValues.size(); i += 2) {
if (name.equalsIgnoreCase(namesAndValues.get(i))) {
namesAndValues.remove(i); // name
namesAndValues.remove(i); // value
i -= 2;
}
}
return this;
}
/**
* Set a field with the specified value. If the field is not found, it is added. If the field
* is found, the existing values are replaced.
* 设置一个具有指定值的字段。如果找不到该字段,则添加该字段。如果找到该字段,则替换现有值。
*/
public Builder set(String name, String value) {
checkNameAndValue(name, value);
removeAll(name);
addLenient(name, value);
return this;
}
private void checkNameAndValue(String name, String value) {
if (name == null) throw new NullPointerException("name == null");
if (name.isEmpty()) throw new IllegalArgumentException("name is empty");
for (int i = 0, length = name.length(); i < length; i++) {
char c = name.charAt(i);
if (c <= '\u0020' || c >= '\u007f') {
throw new IllegalArgumentException(Util.format(
"Unexpected char %#04x at %d in header name: %s", (int) c, i, name));
}
}
if (value == null) throw new NullPointerException("value == null");
for (int i = 0, length = value.length(); i < length; i++) {
char c = value.charAt(i);
if ((c <= '\u001f' && c != '\t') || c >= '\u007f') {
throw new IllegalArgumentException(Util.format(
"Unexpected char %#04x at %d in %s value: %s", (int) c, i, name, value));
}
}
}
/** Equivalent to {@code build().get(name)}, but potentially faster. */
public String get(String name) {
for (int i = namesAndValues.size() - 2; i >= 0; i -= 2) {
if (name.equalsIgnoreCase(namesAndValues.get(i))) {
return namesAndValues.get(i + 1);
}
}
return null;
}
public Headers build() {
return new Headers(this);
}
}
通过注释我们可以看到Builder是对头的操作的一些行为的封装,接着我们再来看 Headers 它里面只有一个全局变量private final String[] namesAndValues;
我们拿我们经常用的public static Headers of(Map
方法来看
public static Headers of(Map headers) {
if (headers == null) throw new NullPointerException("headers == null");
// Make a defensive copy and clean it up.
// 做一个保护性拷贝并清理它。
String[] namesAndValues = new String[headers.size() * 2];
int i = 0;
for (Map.Entry header : headers.entrySet()) {
if (header.getKey() == null || header.getValue() == null) {
throw new IllegalArgumentException("Headers cannot be null");
}
String name = header.getKey().trim();
String value = header.getValue().trim();
if (name.length() == 0 || name.indexOf('\0') != -1 || value.indexOf('\0') != -1) {
throw new IllegalArgumentException("Unexpected header: " + name + ": " + value);
}
namesAndValues[i] = name;
namesAndValues[i + 1] = value;
i += 2;
}
return new Headers(namesAndValues);
}
这里面做了以下几个事情:
判断是否为空
新建临时变量namesAndValues 长度为map*2
遍历map key和value不能为空或者空字符串
-
namesAndValues 一个是name 第二个是value 偶数是name 奇数是value,比如说 :
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2;
那么他这样处理后结果是
namesAndValues[0]="User-Agent"
namesAndValues[1]="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; SLCC2"
-
然后
return new Headers(namesAndValues);
把值赋给全局变量namesAndValues