OkHttpClient
1.OkHttpClient应该创建一个单例,方便所有HTTP请求重用。 这是因为每个客户端都拥有自己的连接池和线程池, 重用这些连接和线程可以减少延迟并节省内存。 相反,为每个请求创建OkHttpClient会浪费空闲池中的资源。
实例化对象两种方式:
// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient();
// The singleton HTTP client.
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
.build();
2.通过newBuilder()
方法自定义OkHttpClient实例。使用构建器提供的方法指定具体的配置项。
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Response response = eagerClient.newCall(request).execute();
3.空闲状态下,线程池和连接池中的线程和连接将自动释放。但是,如果你的应用程序需要主动释放无用的资源,可以使用shutdown()
关闭调度执行器的服务。 这也将导致后续的调用被拒绝。
client.dispatcher().executorService().shutdown();
用evictAll()
清除连接池。 请注意,连接池的守护程序线程可能不会立即退出。
client.connectionPool().evictAll();
如果您的客户端有缓存,请调用close()
。 请注意,针对关闭的缓存创建调用是一个错误,这样做将导致调用崩溃。
client.cache().close();
OkHttp为HTTP / 2连接使用了守护线程。 如果它们保持空闲,将自动退出。
ConnectionSpec
指定socket 连接的配置。针对https:的URL包含了密码套件和TLS来加密连接。只有在SSL套接字中启用了TLS连接规范中配置的TLS版本才能使用。 例如,如果SSL套接字没有启用TLS 1.3,即使连接规范中存在TLS也不会使用。 同样的策略也适用于密码套件。使用ConnectionSpec.Builder.allEnabledTlsVersions()
和ConnectionSpec.Builder.allEnabledCipherSuites
将所有功能选择推迟到底层SSL套接字。
ConnectionPool
管理HTTP和HTTP / 2连接的重用以减少网络延迟。共享相同Address对象的HTTP请求可能共享一个Connection对象。该类使用“保持连接开启”的策略以供后续使用。
Call / RealCall
Call是一个已准备执行的请求。可以被取消。由于此对象表示单个请求/响应对(流),因此无法执行两次。【接口】
public interface Call extends Cloneable {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Call clone();
interface Factory {
Call newCall(Request request);
}
}
RealCall是Call的实现类,持有OkHttpClient和Request的引用,复写了Call的两个关键方法execute()和enqueue(Callback responseCallback)。前者为同步调用,后者为异步调用
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
execute()辗转调用了dispather.execute方法,仅仅是为了将Request添加到一个请求集合中。
RealInterceptorChain
一个具体的拦截器链,其中携带整个拦截器链:所有应用拦截器,OkHttp核心,所有网络拦截器,最后是网络调用者。
链中的拦截器通过调用intercept方法执行具体操作。
- RetryAndFollowUpInterceptor拦截器作用:从故障中恢复,并根据需要遵循重定向。
此拦截器实例化StreamAllocation - BridgeInterceptor拦截器作用:从应用程序到网络的桥梁。首先,它根据用户请求构建网络请求,然后它继续调用网络。最后,它从网络响应中构建用户响应。
处理请求体body和请求头header的配置信息,cookie等 - CacheInterceptor拦截器作用:从缓存中请求数据,写响应数据到缓存中
- ConnectInterceptor拦截器作用:
打开与目标服务器的连接,并进入下一个拦截器。
1.实例化HttpCodec:对HTTP请求编码,对HTTP响应解码。
2.实例化RealConnection - CallServerInterceptor拦截器作用:这是链中的最后一个拦截器。它对服务器进行网络调用。
场景图
采用自增递归(recursive)调用Chain.process()
StreamAllocation
该类协调三个实体之间的关系:
Connections:到远程服务器的物理Socket连接。这些建立可能很慢,所以有必要能够取消当前的连接。
Streams:在连接上分层的逻辑HTTP请求/响应对。每个连接都有自己的分配限制,它定义了连接可以携带多少个并发流。 HTTP / 1.x连接一次可以携带1个流,HTTP / 2通常携带多个。
Calls:流的逻辑序列,通常是初始请求及其后续请求。HTTP请求任务封装。
StreamAllocation: 用来控制Connections/Streams的资源分配与释放
- 选择路线与自动重连(RouteSelector)
此步骤用于获取socket的ip与端口 - 连接socket链路(RealConnection)
此步骤可以进行TCP连接(三次握手) - 释放socket链路(release)
Dispatcher
执行异步请求时的策略。
每个调度程序都使用ExecutorService
在内部运行调用。 如果您提供自己的执行程序,它应该能够并发运行配置的最大调用数。
默认使用了线程池执行器ThreadPoolExecutor,最大请求数64个.
private int maxRequests = 64;//最大请求数
private int maxRequestsPerHost = 5;//每个主机最大请求数
private Runnable idleCallback;//任务执行完毕,空闲的Runnable
/** Executes calls. Created lazily. */
private ExecutorService executorService;//线程池
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//缓存队列,当runningAsyncCalls满后,会添加到此队列
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在运行的任务,仅仅是用来引用正在运行的任务以判断并发量,注意它并不是消费者缓存
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//同步调用集合
Dispatcher有两个构造函数,可以使用自己设定线程池,如果没有设定线程池则会在请求网络前自己创建线程池。
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
异步调用方法,RealCall.enqueue
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
其中AsyncCall是一个Runnable,添加到Deque runningAsyncCalls集合中,由线程池负责执行。
Dispatcher.enqueue()
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
执行的是AsyncCall这个Runnable,即run方法,在这里转化为execute方法了
@Override
protected void execute() {
boolean signalledCallback = false;
try {
//通过拦截器链获取响应信息
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
知识点:
根据可变参数,返回不可修改的集合
/** Returns an immutable list containing {@code elements}. */
public static <T> List<T> immutableList(T... elements) {
return Collections.unmodifiableList(Arrays.asList(elements.clone()));
}