本文目标
搞明白OKHttp的源码同步请求和异步请求基本流程
基本使用
同步请求
public void synRequest() {
//1.创建okHttpClient和创建Request对象
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
//把配置的请求信息封装成Request对象
Request request = new Request
.Builder()
.url("http://www.baidu.com")
.get()
.build();
//2.把Request对象封装成call对象
Call call = client.newCall(request);
try {
//3.发起同步请求
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
异步请求
public void asyRequest() {
//1.创建okHttpClient和创建Request对象
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
Request request = new Request
.Builder()
.url("http://www.baidu.com")
.get().build();
//2.把Request对象封装成call对象
Call call = client.newCall(request);
//3.发起异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//都在子线程
System.out.println("Fail");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//都在子线程
System.out.println(response.body().string());
}
});
}
- 1.创建okHttpClient和创建Request对象(配置的请求信息封装)
- 2.把Request对象封装成call对象
- 3.发起同步请求或异步请求
1.1OkHttpClient
首先先看OkHttpClient内Builder这个内部类
OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
public static final class Builder {
public Builder() {
dispatcher = new Dispatcher();//重要,请求的分发器
...
connectionPool = new ConnectionPool();//连接池
...
}
//最后通过build()方法把配置信息封装成OkHttpClient对象
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
}
可以看到这里面创建了一大堆对象(这里只是挑了重要的对象),不过不要怕,我们看重要的
dispatcher = new Dispatcher();请求的分发器,同步请求放到队列当中来执行
connectionPool = new ConnectionPool();连接池,客户端和服务器的链接由它来统一管理,如果url相同则会复用,各种策略都在这个类中来管理
最后通过build()方法把配置信息封装成OkHttpClient对象
1.2Request
这是Request对象的构建
Request request = new Request.Builder()
.url("http://www.baidu.com")
.get()
.build();
具体源码如下
public final class Request {
final HttpUrl url;//链接
final String method;//请求方式
final Headers headers;//头信息
final @Nullable RequestBody body;//请求体
final Object tag;
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;
}
...
//内部类Builder
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder url(HttpUrl url) {
if (url == null) throw new NullPointerException("url == null");
this.url = url;
return this;
}
...
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}
}
把配置的请求信息封装成Request对象,包含url,method请求方式,headers头信息,RequestBody请求体
2.Call
把Request对象封装成call对象
Call call = client.newCall(request);
首先client.newCall(request);调用进去,会发现是RealCall在调用
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
}
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
private EventListener eventListener;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);//重定向拦截器
}
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.eventListener = client.eventListenerFactory().create(call);
return call;
}
}
可以看出来,RealCall是持有了前面创建的OkHttpClient和Request对象
3.1Call的同步请求
try {
//3.发起同步请求
Response response = call.execute();
System.out.println(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
call.execute();追进去
final class RealCall implements Call {
@Override public Response execute() throws IOException {
synchronized (this) {
// 同一个http请求只能请求一次
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//捕捉堆栈信息
captureCallStackTrace();
//开启了监听事件
eventListener.callStart(this);
try {
//添加到执行队列当中
client.dispatcher().executed(this);
//拦截器链来返回结果
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
//回收请求
client.dispatcher().finished(this);
}
}
}
上面这个方法最核心的就是,把这个同步请求添加到队列当中了,然后才是进入拦截器链中经过若干个拦截器直到最后返回(后面在分析拦截器)
client.dispatcher().executed(this);这一行追进去是下面的代码
public final class Dispatcher {
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque runningSyncCalls = new ArrayDeque<>();
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
}
最后还会回收请求 client.dispatcher().finished(this);
追进去看一下
public final class Dispatcher {
//移除同步请求
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();//计算现有的请求
idleCallback = this.idleCallback;
}
//没有可运行的请求 && idleCallback不为空
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
//计算正在执行的请求数量
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
}
主要就是从当前队列中移除这个同步请求,如果不能移除抛出异常,同步的时候promoteCalls这个参数是false,所以执行不到promoteCalls()这个方法,异步可以执行到,然后调用runningCallsCount()会计算现有的请求,最后没有可运行的请求 && idleCallback不为空就会调用 idleCallback.run();方法,至此同步请求流程结束
总结一下同步请求:
- 保存同步请求
- 移除同步请求
无非就是做了这两件事情
3.2Call的异步请求
//3.发起异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//都在子线程
System.out.println("Fail");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//都在子线程
System.out.println(response.body().string());
}
});
我们从call.enqueue()调用这个方法开始看,里面会传入一个Callback对象用来获取网络成功或者失败的回调,然后我们来看enqueue()方法
final class RealCall implements Call {
@Override
public void enqueue(Callback responseCallback) {
//加锁
synchronized (this) {
//call 这个实例有没有执行过,上面得到的call只能被执行一次,否则就抛出异常
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
//内部类
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
@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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
}
NamedRunnable是继承自Runnable的
public abstract class NamedRunnable implements Runnable {
...
}
client.dispatcher().enqueue(new AsyncCall(responseCallback));我们先来看enqueue()这个方法
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
//线程池
private @Nullable ExecutorService executorService;
//异步等待队列
private final Deque readyAsyncCalls = new ArrayDeque<>();
//异步执行队列
private final Deque runningAsyncCalls = new ArrayDeque<>();
//同步锁
synchronized void enqueue(AsyncCall call) {
//如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
}
在enqueue这个方法中首先是同步的,然后如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求
总结一下 call.enqueue(callback)方法
- 1.判断当前call
- 2.封装成了一个AsyncCall对象
- 3.client.dispatch().enqueue()
判断当前call,call 这个实例有没有执行过,call只能被执行一次,否则就抛出异常,然后通过传递进来的callBack对象我们吧它封装成了一个AsyncCall对象(其实就是个runnable),然后就会调用client.dispatch().enqueue()方法进行异步请求,同时如果正在执行的异步队列 < 64 && 正在执行的主机的请求数 < 5 ,则把请求添加到执行队列中,通过线程池去执行这个请求,如不不是的话就添加到异步等待队列中
接下来我们来看下线程池
public final class Dispatcher {
private @Nullable ExecutorService executorService;
//同步并保证线程池单例
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
}
该线程池最大线程数量为Integer.MAX_VALUE,但是不要担心,在之前限制了 maxRequests=64,就是说最多同时只有64个网络请求,然后该线程池内的线程只能存活60秒,其实该线程池执行的runnable对象是AsyncCall对象,来让我们再次看下这个AsyncCall对象
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
String host() {
return originalRequest.url().host();
}
Request request() {
return originalRequest;
}
RealCall get() {
return RealCall.this;
}
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
//重定向拦截器是否取消了
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
//如果没有取消则调用onResponse()通过回调接口把response返回出去
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 {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
我们发现该AsyncCall是继承自NamedRunnable,来看下
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
发现NamedRunnable继承自Runnable,那也就是说AsyncCall是个runnable,那我们看下run()方法,发现AsyncCall类中没有,那就看父类NamedRunnable的,发现最后调用了execute()方法,那我们可以看AsyncCall的execute()方法,在这里有一行Response response = getResponseWithInterceptorChain();,这才是真正获取响应结果的代码,这是拦截器链(我们后面会分析),然后通过回调接口把数据返出去,在finally中我们又看到了client.dispatcher().finished(this);
public final class Dispatcher {
//异步请求的回收,这次第3个参数为true
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
private void finished(Deque calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
}
该finished方法共做了3件事:
- 1.把这个请求在正在请求的异步队列中移除掉,就是这行代码calls.remove(call)
-
- promoteCalls(); 调整整个异步请求的队列
- 3.重新计算正在运行的往前请求数量并进行赋值
问题1:OKIO是什么?
特点
它也是基于插管的,⽽且是单向的,输⼊源叫 Source,输出⽬标叫Sink。
⽀持 Buffer,和 NIO ⼀样,可以对 Buffer 进⾏操作,但不强制使⽤Buffer。
⽤法
BufferedSource source = Okio.buffer(Okio.source(new
File("./io/text.txt"))));
System.out.println(source.readUtf8Line());
问题2:OKIO 与 IO、NIO 区别
okio 相⽐ io 和 nio,api 更简单易⽤。
⽀持超时机制。
引⼊ ByteString ⽤空间换时间提⾼性能。
采⽤ segment 机制进⾏内存共享,节省复制时间消耗。