目录
一、OkHttp3请求流程图
二、主要流程
2.1 创建一个Http客户端:
2.2 组装Request对象
2.2.1 Request
2.2.2 RequestBody
2.3 请求网络核心代码(同步)
2.4 请求网络核心代码(异步)
2.4.1 Call接口
2.4.2 newCall()
2.4.3 RealCall.newRealCall()
2.4.4 RealCall.execute()
2.4.5 RealInterceptorChain.proceed()
2.4.6 Dispatcher
2.4.7 Response
三、主要参考
OkHttpClient client = new OkHttpClient();
/** OkHttpClient()调用链条 */
/** 贴出源码,旨在有需要时可直接索引重要参数*/
public OkHttpClient() {
this(new Builder());
}
/** 传入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 = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
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.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
/** Builder是OkHttpClient的静态内部类 @OkHttpClient.class*/
// public static final class Builder {}
// Builder类重要参数
Dispatcher dispatcher;
@Nullable Proxy proxy;
List protocols;
List connectionSpecs;
final List interceptors = new ArrayList<>();
final List networkInterceptors = new ArrayList<>();
EventListener.Factory eventListenerFactory;
ProxySelector proxySelector;
CookieJar cookieJar;
@Nullable Cache cache;
@Nullable InternalCache internalCache;
SocketFactory socketFactory;
@Nullable SSLSocketFactory sslSocketFactory;
@Nullable CertificateChainCleaner certificateChainCleaner;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
Authenticator authenticator;
ConnectionPool connectionPool;
Dns dns;
boolean followSslRedirects;
boolean followRedirects;
boolean retryOnConnectionFailure;
int callTimeout;
int connectTimeout;
int readTimeout;
int writeTimeout;
int pingInterval;
// 构造方法
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
if (proxySelector == null) {
proxySelector = new NullProxySelector();
}
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
certificatePinner = CertificatePinner.DEFAULT;
proxyAuthenticator = Authenticator.NONE;
authenticator = Authenticator.NONE;
connectionPool = new ConnectionPool();
dns = Dns.SYSTEM;
followSslRedirects = true;
followRedirects = true;
retryOnConnectionFailure = true;
callTimeout = 0;
connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;
pingInterval = 0;
}
Okhttp中有client.internalCache()和client.connectionPool()两个重要的概念,前者管理网络访问的缓存信息,后者用于存储已链接的RealConnection(该RealConnection已经跟对应的hostname完成了三次握手)。
/**
cache域在我们构造OkHttpClient的时候是没有被初始化的,因此如果我们没有通过
调用Builder的cache方法设置cache值的话,该方法返回的对象实际上是一个不支持任何
缓存操作的对象,即该对象的所有方法为空。因此如果需要OkHttpClient支持缓存,需要
我们写一个Cache对象并在构造OkHttpClient的时候将其传给OkHttpClient。
*/
/** internalCache()@OkHttpClient.class */
InternalCache internalCache() {
return cache != null ? cache.internalCache : internalCache;
}
/** cache()@[email protected] */
/** 完成cache的初始化,如果不调用该方法那么OkHttpClient默认不提供Cache功能;*/
public Builder cache(Cache cache) {
this.cache = cache;
this.internalCache = null;
return this;
}
/** connectionPool()@OkHttpClient.class */
/** connectionPool的初始化是在构建OkHttpClient时创建的,调用的构造器为new ConnectionPool();*/
public ConnectionPool connectionPool() {
return connectionPool;
}
采用建造者(Builder)模式创建
/** Request类主要变量 */
private final HttpUrl url; //目标地址
private final String method; //方法
//请求头,Headers.class里面维护了一个private final String[] namesAndValues;数据集
private final Headers headers;
private final RequestBody body; //请求表单
private final Object tag; //标签
private volatile URI javaNetUri; // Lazily initialized.
private volatile CacheControl cacheControl; // Lazily initialized.
/** 内部Builder类 */
/** public static class Builder {} */
/** 构造函数 */
public Builder() {
// 默认是 GET 方法
this.method = "GET";
this.headers = new Headers.Builder();
}
/** build()方法 */
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
// GET示例
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
对于Post请求还须使用Request.Builder().post()方法,并传入RequestBody参数,可以为null;
/** RequestBody.class */
/** 多个重载的create()方法 */
/**
* Returns a new request body that transmits {@code content}. If {@code contentType} is non-null
* and lacks a charset, this will use UTF-8.
*/
/** JSON类型RequestBody */
public static RequestBody create(@Nullable MediaType contentType, String content) {
Charset charset = UTF_8;
if (contentType != null) {
charset = contentType.charset();
if (charset == null) {
charset = UTF_8;
contentType = MediaType.parse(contentType + "; charset=utf-8");
}
}
byte[] bytes = content.getBytes(charset);
return create(contentType, bytes);
}
/** Returns a new request body that transmits the content of {@code file}. */
/** 文件类型RequestBody */
public static RequestBody create(final @Nullable MediaType contentType, final File file) {
if (file == null) throw new NullPointerException("file == null");
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return file.length();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
try (Source source = Okio.source(file)) {
sink.writeAll(source);
}
}
};
}
/** Returns a new request body that transmits {@code content}. */
public static RequestBody create(final @Nullable MediaType contentType,
final ByteString content) {
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() throws IOException {
return content.size();
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content);
}
};
}
/** Returns a new request body that transmits {@code content}. */
public static RequestBody create(final @Nullable MediaType contentType,
final byte[] content) {
return create(contentType, content, 0, content.length);
}
/** Returns a new request body that transmits {@code content}. */
/** 字节流类型RequestBody */
public static RequestBody create(final @Nullable MediaType contentType, final byte[] content,
final int offset, final int byteCount) {
if (content == null) throw new NullPointerException("content == null");
Util.checkOffsetAndCount(content.length, offset, byteCount);
return new RequestBody() {
@Override public @Nullable MediaType contentType() {
return contentType;
}
@Override public long contentLength() {
return byteCount;
}
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.write(content, offset, byteCount);
}
};
}
// POST之RequestBody示例
// 数据类型为json格式
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
// json数据
String jsonStr = "{\"key_one\":\"value_one\",\"key_two\":\"value_two\"}";
RequestBody body = RequestBody.create(josnStr, JSON);
client.newCall(request).execute();
client.newCall(request).enqueue(new Callback{...});
不管是同步还是异步,都需要获取Call对象,然后调用不同的线程管理方法去执行网络请求,下面详解。
/**
* A call is a request that has been prepared for execution. A call can be canceled.
* As this object represents a single request/response pair (stream),
* it cannot be executed twice.
* Call是用来执行的具体请求,可以被取消,但因为其是个流对象,所以只能执行一次。
*/
public interface Call extends Cloneable {
/**
* 请求方法
*/
Request request();
/**
* 同步请求方法
*/
Response execute() throws IOException;
/**
* 异步请求方法
*/
void enqueue(Callback responseCallback);
/**
* 取消请求调用
*/
void cancel();
/**
* 判断请求调用是否执行
*/
boolean isExecuted();
/**
* 判断请求调用是否取消
*/
boolean isCanceled();
/**
* 克隆一个请求调用
*/
Call clone();
/**
* 工厂接口,OkHttpClient类重写
*/
interface Factory {
Call newCall(Request request);
}
}
/**
* OkHttpClient方法
* Prepares the {@code request} to be executed at some point in the future.
* 准备request网络请求 在将来某个时候执行。
*/
@Override
public Call newCall(Request request) {
/** this 即 OkHttpClient */
return RealCall.newRealCall(this, request, false /* for web socket */);
}
/** [email protected] */
/** RealCall类的静态方法 */
static RealCall newRealCall(OkHttpClient client, Request originalRequest,
boolean forWebSocket){
// Safely publish the Call instance to the EventListener.
RealCall call = new RealCall(client, originalRequest, forWebSocket);
call.transmitter = new Transmitter(client, call);
return call;
}
/** 实际上调用的构造函数 */
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
final EventListener.Factory eventListenerFactory = client.eventListenerFactory();
// 传入的OkHttpClient对象
this.client = client;
// 传入的Request请求对象
this.originalRequest = originalRequest;
// 是否是webSocket请求,默认false
this.forWebSocket = forWebSocket;
// 一个可以从故障中恢复的拦截器
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
/** [email protected] */
/** 同步网络请求 */
@Override
public Response execute() throws IOException {
// 1.当前请求已经执行过 抛异常
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
// 2.跟踪超时等运行时数据
transmitter.timeoutEnter();
transmitter.callStart();
try {
// 3.执行同步网络请求操作
client.dispatcher().executed(this);
// 4.返回结果
return getResponseWithInterceptorChain();
} finally {
// 5.结束当前网络请求
client.dispatcher().finished(this);
}
}
/** [email protected] */
/** 执行网络请求,并返回结果 */
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
// 建立一个完整的拦截器堆栈
List interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
// 1.重试与重定向拦截器
interceptors.add(new RetryAndFollowUpInterceptor(client));
// 2.桥接拦截器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
// 3.请求是从内部缓存中发出,缓存拦截器将返回写入到响应缓存中和此请求对应
interceptors.add(new CacheInterceptor(client.internalCache()));
// 4.打开客户端与目标服务器的连接-->连接服务器
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
// 5.如果不是WebSocket长链接,添加网络拦截器
interceptors.addAll(client.networkInterceptors());
}
// 6.拦截器链中最后一个拦截器,对服务器进行请求呼叫
interceptors.add(new CallServerInterceptor(forWebSocket));
// 7.创建拦截器链的实例
Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
originalRequest, this, client.connectTimeoutMillis(),
client.readTimeoutMillis(), client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
// 真正开始执行网络请求,并得到返回结果
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
/** [email protected] */
/** 执行网络请求,并返回结果 */
public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.exchange != null && !this.exchange.connection().supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.exchange != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
// 递归调用,返回Response
RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (exchange != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
对于很多异步请求,请求的执行顺序,执行规则均由分发器决定的,每个分发器的内部有个ThreadPoolExecutor,这是线程池的核心类,其主要作用是将线程进行复用,每个请求需要一个线程去执行,每次都重新创建新的线程太消耗性能,因此对于有已经执行完上一个任务的线程进行复用,不需要再重新创建一个新的线程去执行下一个任务。
Dispatcher内部主要方法:
/** Dispatch.class */
/**
每一个请求都创建了一个RealCall实例,异步请求AsyncCall是RealCall的内部类;
每个Call都需要一个线程去任务,执行Call的过程其实就是执行线程的excute()过程。
当Dispatcher接收到一个请求时,Dispatcher负责在其内部维护的线程池中找出空闲的线程去执行其execute()方法。
*/
/** 主要变量 */
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
/** 异步请求等待队列 */
private final Deque readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
/** 异步请求队列(执行中/已取消但还未结束) */
private final Deque runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
/** 同步请求队列(执行中/已取消但还未结束) */
private final Deque runningSyncCalls = new ArrayDeque<>();
/** 连接池 */
public synchronized ExecutorService executorService() {
if (executorService == null) {
// 本质上是ThreadPoolExecutor
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
/** ================== 同步 =================== */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** ================== 异步 =================== */
void enqueue(AsyncCall call) {
synchronized (this) {
// 直接加入到等待队列
readyAsyncCalls.add(call);
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing
// running call to the same host.
// 如果不是WebSocket长链接
if (!call.get().forWebSocket) {
// 判断当前call的host是否可以用执行中的某个call的host复用
AsyncCall existingCall = findExistingCallWithHost(call.host());
// 复用host
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
// 执行网络请求
promoteAndExecute();
}
/** 判断当前的网络请求的host在 当前正在进行的 网络请求队列中是否已存在 */
@Nullable
private AsyncCall findExistingCallWithHost(String host) {
for (AsyncCall existingCall : runningAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
for (AsyncCall existingCall : readyAsyncCalls) {
if (existingCall.host().equals(host)) return existingCall;
}
return null;
}
/** 执行网络请求 */
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// Max capacity.
if (runningAsyncCalls.size() >= maxRequests) break;
// Host max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
// 执行
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
/** RealCall请求执行结束后调用 */
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
private void finished(Deque calls, T call) {
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
Dispatcher总结:
(1)runningAsyncCalls存放正在执行的请求,readyAsyncCalls存放等待执行的请求;
(2)该类中有一个newCachedThreadPool线程执行器,利用该执行器来执行异步请求的execute方法。也就是说异步请求发送在非当前工作线程,即创建异步请求的线程,而是从线程池中获取一条线程执行网络请求。同步请求则直接是在当前工作线程中执行。
(3)该类对异步请求的管理是通过maxRequests、maxRequestsPerHost进行控制的,前者控制线程池中同时运行的最大请求数,防止同时运行线程过多,造成OOM。后者限制了同一hostname下的请求数,防止一个应用占用的网络资源过多,优化用户体验。
Response.class
/** Response.class 主要变量与方法 */
private final Request request; //对应的request
private final Protocol protocol; //对应的Http协议
private final int code; //返回状态码
private final String message; //Http状态对应的消息
private final Handshake handshake; //TLS握手协议Transport Layer Security
private final Headers headers; //返回响应头
private final ResponseBody body; //Http表单
private Response networkResponse; //来源于网络的Response,如果响应来自缓存,则该值为null
private Response cacheResponse; //来自缓存的响应
private final Response priorResponse; //在redirect或者授权改变的时候,该结果不为空
private volatile CacheControl cacheControl; // Lazily initialized.
@Override public String toString() {
return "Response{protocol="
+ protocol
+ ", code="
+ code
+ ", message="
+ message
+ ", url="
+ request.url()
+ '}';
}
/** Returns the HTTP status code. */
public int code() {
return code;
}
/**
* Returns true if the code is in [200..300), which means the request was successfully
* received, understood, and accepted.
*/
public boolean isSuccessful() {
return code >= 200 && code < 300;
}
/** Returns the HTTP status message. */
public String message() {
return message;
}
/**
* Returns a non-null value if this response was passed to {@link Callback#onResponse} or
* returned from {@link Call#execute()}. Response bodies must be {@linkplain ResponseBody
* closed} and may be consumed only once.
*
* This always returns null on responses returned from {@link #cacheResponse}, {@link
* #networkResponse}, and {@link #priorResponse()}.
*/
public @Nullable ResponseBody body() {
return body;
}
ResponseBody.class
/** ResponseBody.class 主要变量与方法 */
/** Multiple calls to {@link #charStream()} must return the same instance. */
private @Nullable Reader reader;
public abstract @Nullable MediaType contentType();
/**
* Returns the number of bytes in that will returned by {@link #bytes},
* or {@link #byteStream}, or -1 if unknown.
*/
public abstract long contentLength();
public abstract BufferedSource source();
/** 输入流 */
public final InputStream byteStream() {
return source().inputStream();
}
/**
* Returns the response as a string.
*
* If the response starts with a
* Byte Order Mark (BOM),
* it is consumed and used to determine the charset of the response bytes.
*
*
Otherwise if the response has a Content-Type header that specifies a charset,
* that is used to determine the charset of the response bytes.
*
*
Otherwise the response bytes are decoded as UTF-8.
*
*
This method loads entire response body into memory. If the response body is very large
* this may trigger an {@link OutOfMemoryError}. Prefer to stream the response body if this
* is a possibility for your response.
*/
public final String string() throws IOException {
try (BufferedSource source = source()) {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
}
}
注意事项:
1、Response.code是http响应行中的code,如果访问成功则返回200.这个不是服务器设置的,而是http协议中自带的。res中的code才是服务器设置的。注意二者的区别。
2、response.body().string()本质是输入流的读操作,所以它还是网络请求的一部分,所以这行代码必须放在子线程。
3、response.body().string()只能调用一次,在第一次时有返回值,第二次再调用时将会返回null。原因是:response.body().string()的本质是输入流的读操作,必须有服务器的输出流的写操作时客户端的读操作才能得到数据。而服务器的写操作只执行一次,所以客户端的读操作也只能执行一次,第二次将返回null。
参考链接(1):https://github.com/square/okhttp
参考链接(2):https://blog.csdn.net/qq_17470165/article/details/80543537
参考链接(3):https://blog.csdn.net/suyimin2010/article/details/80196443