一、okhttp发起网络请求的步骤
- 构建OkHttpClient客户端
- 构建Request请求
- 创建Call对象,发起网络请求
- 处理Response响应结果
1.OkHttpClient
可以通过new OkHttpClient()创建默认配置的client对象,也可以通过内置的Builder动态设置所需要的属性,可设置拦截器、缓存文件缓存大小、https验证一系列的值。
2.request
里面封装了请求的一些字段
- 请求方法:
常用的get、post方法。 - 请求url:
一个封装好的HttpUrl对象。 - headers:
请求头信息,可通过addHeader跟setHeader设置。 - 请求体RequestBody:
是一个抽象类,官方提供了两个实现类,FormBody(表单提交),MultipartBody(文件提交),分别对应两种不同MIME类型。 - CacheControl:
控制网络请求是否使用缓存。
3.Response
Headers+ResponseBody组成,ResponseBody是抽象类,有两个实现的子类:RealResponseBody、CacheResponseBody,分别代表真实响应跟缓存响应。
4.Call
http请求任务的封装,网络请求的操作基本都定义在这个接口里面,是OkHttp的核心类。当调用OkHttpClient的newCall方法时,内部是创建了一个RealCall对象。需要传入3个参数,client对象、request、是否是WebSocket。
/**
* Prepares the {@code request} to be executed at some point in the future.
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
5.RealCall
创建了Call对象后,里面有两个方法是进行网络请求访问的,分别是:execute(同步请求),enqueue(异步请求),我们先转到execute方法的源码。
@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);
}
}
- 代码可以看到,该方法只能执行一次,再执行的话就会抛出异常
- captureCallStackTrace是捕获请求的StackTrace,后面再详细研究
- 然后就走到Dispatcher的executed方法,Dispatcher是在创建OkHttpClient时就帮我们初始化的一个类
public static final class Builder {
// ...省略相关代码
Dispatcher dispatcher
public Builder() {
dispatcher = new Dispatcher();
}
// ...省略相关代码
}
进到Dispatcher的executed方法
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
把创建的call对象添加到一个叫做runningSyncCalls的对象中,再看源码
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 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里面定义了3个双向队列,看注解不难发现,发现runningSyncCalls是存储的是同步请求,readyAsyncCalls是存放等待执行任务,runningAsyncCalls是存放异步请求任务。
- 执行完client.dispatcher().executed(this),要走到getResponseWithInterceptorChain方法
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(retryAndFollowUpInterceptor);
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
里面创建了一个拦截器的集合,把我们自己定义以及官方定义的拦截器都添加到集合中,通过调用RealInterceptorChain的proceed方法启动第一个拦截器的拦截方法,我们进到RealInterceptorChain的proceed方法源码的核心代码
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
// ...省略若干代码
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(
interceptors, streamAllocation, httpCodec, connection, index + 1, request);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// ...省略若干代码
return response;
}
会通过改变index的值,遍历我们传进去的拦截器集合,每次都会调用拦截器的intercept方法,而intercept方法中会调用RealInterceptorChain的proceed方法进而调用下一个拦截器的intercept方法,直到遍历到最后一个拦截器,而最后一个拦截器拿到响应的内容后,会递归的传递回第一个拦截器。最终又会到RealCall的executed方法,拿到Response。
6.异步请求源码跟踪
- 入口: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));
}
前面两步都是跟同步请求一样
- 进到Dispatcher的enqueue方法之前,先来看看AsyncCall,就是实现了Runnable接口的一个对象,再看enqueue方法
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
//如果正在执行的请求小于设定值即64,并且请求同一个主机的request小于设定值即5
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//添加到执行队列,开始执行请求
runningAsyncCalls.add(call);
//获得当前线程池,没有则创建一个
executorService().execute(call);
} else {
//添加到等待队列中
readyAsyncCalls.add(call);
}
}
会先判断当前异步请求是否大于64,以及与当前请求共享主机的请求数是否大于5,如果都在最大数范围,就会把请求添加到异步请求队列中,并启动线程处理当前请求。否则,就会把请求放在等待执行任务队列中。
- 再看executorService()方法
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;
}
// SynchronousQueue:(同步队列)这个队列接收到任务的时候,会直接提交给线程处理,而不保留它(名字定义为 同步队列)。但有一种情况,假设所有线程都在工作怎么办?
这种情况下,SynchronousQueue就会新建一个线程来处理这个任务。所以为了保证不出现(线程数达到了maximumPoolSize而不能新建线程)的错误,使用这个类型队列的时候,
maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大,去规避这个使用风险。
通过懒加载方式创建一个线程池,执行ExecutorService的execute方法,会执行Runnable的run方法,进而执行AsyncCall的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);
}
}
首先也是通过拦截器完成一系列的网络请求,拿到Response后,再通过一开始调用的call.enqueue方法传进去的callback对象,把成功与失败对调返回。
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
}
});
- OkHttp大体流程
二、核心类
整体流程图