这个库一直想要去学习,不过一直没有时间去做,打算一点点梳理Okhttp。本文将按照发起网络请求的顺序来详细描述下okhttp是如何发起网络请求的,其中包含一些优秀文章可供大家参考。
文章目录
使用实例
先放一段Okhttp的使用代码。
//1
OkHttpClient client=new OkHttpClient();
//2
Request request = new Request.Builder()
.url("url)
.build();
//3
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String results = response.body().string();
}
});
我们阅读源码就要从暴露给用户使用的那个类先去入手,对应Okhttp就是**(创建)OkhttpClient**,以及Request,Callback,在新写Callback的时候要重写onFailure(Call call, IOException e)与OnResponse(Call call, Response response)两个函数,于是另一个重要的类Response类简单来说,使用的时候,只需要传入网络的url地址,然后加上新创建的Callback对象(包括重写两个函数)就完成了对url地址的访问(获取数据或上传数据)。
整体流程
1.创建OkhttpClient实例
在读源码之前,每个类都会有一些注释,先阅读下注释。
1.1 用户使用时,创建全局(单例)实例
这里的意思就是说,最好在整个应用中只包含一个(全局)OkhttpClient实例,在每一个发起Http请求的时候重用该实例即可(单例),同时复用的还有Response缓存、共用线程池以及共用连接池。
* <h3>OkHttpClients should be shared</h3>
*
* <p>OkHttp performs best when you create a single {@code OkHttpClient} instance and reuse it for
* all of your HTTP calls. This is because each client holds its own connection pool and thread
* pools. Reusing connections and threads reduces latency and saves memory. Conversely, creating a
* client for each request wastes resources on idle pools.
1.2 构造方式
可以使用两种方式创建OkHttpClient的实例(构造函数和使用Builder),以及一种特殊的构造方式。
还是先看注释。
<p>Use {@code new OkHttpClient()} to create a shared instance with the default settings:
* <pre> {@code
*
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient();
* }</pre>
*
* <p>Or use {@code new OkHttpClient.Builder()} to create a shared instance with custom settings:
* <pre> {@code
*
* // The singleton HTTP client.
* public final OkHttpClient client = new OkHttpClient.Builder()
* .addInterceptor(new HttpLoggingInterceptor())
* .cache(new Cache(cacheDir, cacheSize))
* .build();
* }</pre>
我们来读源码。
这是第一种默认的方式
public OkHttpClient() {
this(new Builder());
}
最后会调用该函数,使用的就是默认的Builder
private OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
...
}
第二种方式:想要配置OkhttpClient的一些参数,就需要使用Builder。
public final OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor())
.cache(new Cache(cacheDir, cacheSize))
...//其他设置
.build();
最后调用Builder类内的build函数,这里的this就是Builder,所以会调用OkhttpClient(Builder builder)来更改client的数据域。
public OkHttpClient build() {
return new OkHttpClient(this);
}
总结:OkHttpClient本身不能设置参数,需要借助于其内部类Builder设置参数,参数设置完成后,调用Builder的build方法得到一个配置好参数的OkHttpClient对象。这些配置的参数会对该OkHttpClient对象所生成的所有HTTP请求都有影响。
第三种方式:我们先看注释中的文字。
您可以使用{@link#NewBuilder()}自定义共享的OkHttpClient实例。 这将构建一个共享相同连接池,线程池和配置的客户端。 使用生成器方法为特定目的配置派生客户端。
共享相同连接池,线程池和配置的客户端,共享谁的呢?当然是共享那个client实例的。有时候我们想单独给某个网络请求设置特别的几个参数,比如只想设置某个请求的超时时间,但是还想保持OkHttpClient对象(client)中的其他的参数设置,那么可以调用OkHttpClient对象的newBuilder()方法。
* <h3>Customize your client with newBuilder()</h3>
*
* <p>You can customize a shared OkHttpClient instance with {@link #newBuilder()}. This builds a
* client that shares the same connection pool, thread pools, and configuration. Use the builder
* methods to configure the derived client for a specific purpose.
*
* <p>This example shows a call with a short 500 millisecond timeout: <pre> {@code
*
* OkHttpClient eagerClient = client.newBuilder()
* .readTimeout(500, TimeUnit.MILLISECONDS)
* .build();
* Response response = eagerClient.newCall(request).execute();
* }</pre>
这里的this是Okhttp的实例
public Builder newBuilder() {
return new Builder(this);
}
之后会调用Builder类中的这个构造函数,这样就在builder中保存了OkHttpClient中的参数。
Builder(OkHttpClient okHttpClient) {
this.dispatcher = okHttpClient.dispatcher;
this.proxy = okHttpClient.proxy;
this.protocols = okHttpClient.protocols;
this.connectionSpecs = okHttpClient.connectionSpecs;
...
}
1.3 优点
不需要将其杀掉。
保持空闲的线程和连接将自动释放。
<h3>Shutdown isn't necessary</h3>
1.4 涉及的模式
1.构造者模式
不用说了,都有builder了,肯定就是构造者模式了。
2.单例模式
有点单例模式的意思,但这里没有,就是我们构建的OkhttpCilent最好要单例,只构造一个来复用。
3.外观模式
用户可以使用这个类来进行一些操作来实现内部的功能。
2.构造Request对象
构造方法
Request也是需要通过构造者模式来构造。
private 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中的部分代码,可以看到无参构造方法时Builder会默认method为get,其实我们可以简单理解为就是需要将url传给Request,以便于它发送网络请求。也可以看到我们如果想要构建Request对象就一定要构建一个Builder对象,因为Builder类中的每个设置参数的函数都是返回this,所以可以使用链式结构赋值,最后使用build构建时,直接调用new Request(this),这里的this是Builder对象。
//使用代码
Request request=new Request.Builder().url(address).build();
//源码
public static class Builder {
private HttpUrl url;
private String method;
private Headers.Builder headers;
private RequestBody body;
private Object tag;
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
private Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
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);
}
get与post
get方式很简单,因为我们RequestBuilder的无参构造函数默认将method设置为GET。
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
而post方式则不同,我们需要借助RequestBody类
与post方法。因为post方法会将所上传的信息封装在请求数据中并发给服务器,这里请求数据就是RequestBody,具体代码如下。
首先要创建一个RequestBody(传入当前MediaType类型和json数据),同时要调用Builder的post方法,并传入RequestBody实例。在post方法中会将method置为post,对body赋值。
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
}
3.同步模式与异步模式
3.1 简介
当构建完Request之后,我们就需要构建一个Call,Okhttp中使用异步模式就是使用enqueue
方法,而同步模式就是使用execute
方法。同步方式没有传入Callback对象,我们可以通过判断返回的response的isSuccessfull字段来判断是否请求成功。
同步方式:
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new IOException("Unexpected code " + response);
}
异步方式:enqueue()方法的内部已经开好子线程了,在子线程中去执行HTTP请求,并将最终的请求结果回调OKhttp.Callback当中,在调用它的时候要重写onResponse(),得到服务器返回的具体内容,重写onFailure(),在这里对异常情况进行处理。不可以再这里进行UI操作。如果要进行UI操作一定要通过runOnUiThread(new Runnable(){});
切换回UI线程。
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(WeatherActivity.this, "获取天气信息失败",
Toast.LENGTH_SHORT).show();
//刷新事件结束,隐藏刷新进度条
swipeRefresh.setRefreshing(false);
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String results = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
...
}
});
}
});
3.2 异步方式
3.2.1 newcall()方法
这里我们是使用异步方式,所以先看异步的代码。
client.newCall(request).enqueue(callback);
我们回到OkhttpClient,去看newCall方法,发现返回了的其实是 RealCall对象。
/**
* 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 */);
}
能看到OkHttpClient是实现了接口Call.Factory,很明显这里是工厂模式。将(Call)构建的细节交给实现它的类实现,顶层只需要返回Call对象即可,我们不管是怎么构建的call。
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {...}
interface Factory {
Call newCall(Request request);
}
继续回到RealCall方法,能看到刚刚调用的构造函数,基本赋值包括OkHttpClient,Request,以及一个boolean变量,这里默认是给false。
RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
涉及到知识盲点了,默认创建一个RetryAndFollowUpInterceptor对象,这个类被叫做过滤器/拦截器。
3.2.2 enqueue()方法
现在Call创建完成(RealCall),接下来将请求加入调度,代码如下:
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
...
}
@Override
public void onResponse(Call call, Response response) throws IOException {
...
}
});
这里是调用了Call对象的enqueue方法,但其实是RealCall对象的enqueue方法,继续回到RealCall类。可以看到这里使用了synchronized加入了对象锁,防止多线程同时调用,如果用过就会抛出异常,没有用过就会将boolean变量赋值为true。
百度百科:synchronized
synchronized方法后面的括号中是对象,就是对象锁,如果线程进入,则得到当前对象锁,那么别的线程在该类所有对象上的任何操作都不能进行。
之后使用了captureCallStackTrace()方法。
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
captureCallStackTrace()方法中相当于为RetryAndFollowUpInterceptor对象(拦截器)加入了一个用于追踪堆栈信息的callStackTrace。
private void captureCallStackTrace() {
Object callStackTrace = Platform.get().getStackTraceForCloseable("response.body().close()");
retryAndFollowUpInterceptor.setCallStackTrace(callStackTrace);
}
之后使用了 client.dispatcher().enqueue(new AsyncCall(responseCallback));
创建了一个AsyncCall的实例。
看下 AsyncCall类,这里可以简单先理解为把Callback对象先存储在该类中,之后会叙述该类的作用。
final class AsyncCall extends NamedRunnable {
private final Callback responseCallback;
AsyncCall(Callback responseCallback) {
super("OkHttp %s", redactedUrl());
this.responseCallback = responseCallback;
}
...
}
之后看下client.dispatcher()方法返回了OkHttpClient类中的Dispatcher对象(调度器)。该Dispatcher对象是在OkHttpClient对象构造时,获取Bulider对象中的Dispatcher对象。(总结一下:所有OkHttpClient中的数据域对象,全是Builder类中的对象,在Builder类构造函数中被赋值)
public Dispatcher dispatcher() {
return dispatcher;
}
3.2.3 Dispatcher
进入Dispathcer类,首先看该类的成员变量(数据域)。
//最大异步请求数为64
private int maxRequests = 64;
//每个主机的最大请求数为5
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
//线程池
/** Executes calls. Created lazily. */
private ExecutorService executorService;
//等待队列(准备执行的异步请求)
/** 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<>();
我好菜,连Deque是双端队列都不知道。简单来说双端队列就是可以对队首和队尾插入和移除元素。这里稍微补充一下Deque的方法。
看起来与LinkedList有点相似。都是即可作为栈也可以作为队列
之后去看该类的enqueue方法。该方法进行了上锁,线程获得了成员锁,即一次只可以有一个线程访问该方法,其他线程想要访问的时候就会被阻塞,需要等到该线程用完才可以访问。
synchronized void enqueue(AsyncCall call) {
//判断正在执行的异步请求数没有达到阈值,并且该对象需要的主机请求数也没有达到阈值
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
//加入到执行队列,并立即执行。
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
//加入等待队列
readyAsyncCalls.add(call);
}
}
runningCallsForHost函数 返回该请求(AsyncCall对象)需要的主机请求数。
/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.host().equals(call.host())) result++;
}
return result;
}
加入到执行队列后,还有一行代码。可以理解为使用线程池创建一个线程去执行该请求(任务)。
这里可以看到放入线程池中,线程池会创建一个线程去执行该请求,因此前面说的enqueue中会为任务自己开启线程去执行。
executorService().execute(call);
executorService()方法是通过懒汉单例模式创建了一个线程池。各个参数的含义如下
- 0(corePoolSize):核心线程的数量为 0,空闲一段时间后所有线程将全部被销毁。
- Integer.MAX_VALUE(maximumPoolSize): 最大线程数,当任务进来时可以扩充的线程最大值,相当于无限大。
- 60(keepAliveTime): 非核心线程的处于空闲后的最大存活时间。
- TimeUnit.SECONDS:存活时间的单位是秒。
- new SynchronousQueue():工作队列,先进先出。
- Util.threadFactory(“OkHttp Dispatcher”, false):单个线程的工厂 ,通过线程工厂创建线程。
简单来说,在实际运行中,当收到10个并发请求时,线程池会创建十个线程,当工作完成后,线程池会在60s后相继关闭所有线程。(因为都是非核心线程)
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;
}
execute()方法可以理解为将任务添加到线程去执行(当将一个任务添加到线程池中的时候,线程池会为每个任务创建一个线程,该线程会在之后的某个时刻自动执行。)。
什么样的类叫做任务呢。任务就是一个实现了Runnable接口的类。线程就会在接下来自动执行(可以理解为执行该任务类的run()方法)。
综上所述,可以总结下enqueue方法。
该方法的核心思想如下:
首先做出判断:
1执行队列的大小(队列中AsyncCall的数量也相当于正在执行的异步请求数)是否小于阈值
2.执行该请求(AsyncCall对象)的主机请求数是否小于主机的最大请求数。
满足1和2就将该请求(任务)添加到执行队列,立刻执行任务(将任务添加到线程池之后,线程池会为任务创建一个线程),否则进入等待队列。
接下来要看一下我们的从后台获取的数据是从哪里返回的。
3.2.4 AsyncCall
前面说到简单先理解AsyncCall存储了我们的Callback对象,而且使用过Okhttp的人都知道,返回数据的操作都是在Callback对象的两个函数(onFailure()与OnResponse()方法)中。
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String results = response.body().string();
}
});
那么这两个方法是在哪里调用的呢,别跟我说是请求成功和请求失败的时候调用的。我说的是具体位置好吧。
我们把目光放到AsyncCall类去,AsyncCall是RealCall的内部类。
为什么要看该类,因为线程执行的是该任务,我们去找下他是怎么实现的Runnable接口,也可以想到数据应该是在run()函数中获取到的。
final class AsyncCall extends NamedRunnable
可以看到他继承自NamedRunnable,我们进入该抽象类。没的说,该类实现了Runnable接口。可以看到run函数中执行的是execute()函数,execute函数是一个抽象的函数。那么一目了然,AsyncCall类继承该抽象类就一定会实现execute()函数。同时,将一个AsyncCall任务添加到线程池中,线程在执行的就是所传入任务(AsyncCall实例)的execute()函数。
/**
* Runnable implementation which always sets its thread name.
*/
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();
}
接下来看AsyncCall类的execute()方法。果不其然,首先通过**getResponseWithInterceptorChain()**方法获得Response,之后根据判断过滤器retryAndFollowUpInterceptor是否被取消了,来判断请求成功还是失败进而回调Callback的onFailure
还是onResponse
。在回调函数中传递RealCall与response/IOException。那么核心就是getResponseWithInterceptorChain()方法了。
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 {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
3.2.5 getResponseWithInterceptorChain
接着看getResponseWithInterceptorChain的代码,简单来看,首先创建了一个List用来存储过滤器,其实在创建Okhttp对象的时候,都帮我们默认实现了这些过滤器。这里涉及了每个过滤器完成自己的任务,互不耦合。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
//失败和重定向过滤器
interceptors.add(retryAndFollowUpInterceptor);
//封装request和response过滤器
interceptors.add(new BridgeInterceptor(client.cookieJar()));
//缓存相关的过滤器,负责读取缓存直接返回、更新缓存
interceptors.add(new CacheInterceptor(client.internalCache()));
//负责和服务器建立连接
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
//配置 OkHttpClient 时设置的 networkInterceptors
interceptors.addAll(client.networkInterceptors());
}
//负责向服务器发送请求数据、从服务器读取响应数据(实际网络请求)
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(
interceptors, null, null, null, 0, originalRequest);
return chain.proceed(originalRequest);
}
之后执行过滤器,其他可以先不考虑,单单看index,这个index是干嘛用的呢,我们继续向下看代码。
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, Connection connection, int index, Request request) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
}
创建RealInterceptorChain后,会调用该对象的proceed方法,直接去看下面的第二个方法。
@Override public Response proceed(Request request) throws IOException {
return proceed(request, streamAllocation, httpCodec, connection);
}
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
Connection connection) 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.httpCodec != null && !sameConnection(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.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// 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);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != 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");
}
return response;
}
首先就有下面的代码,interceptors是我们之前创建RealInterceptorChain对象时传入的list。
if (index >= interceptors.size()) throw new AssertionError();
省略两个判断,直接到这里,注释写着调用链中的下一个拦截器。而且创建了新的RealInterceptorChain也使用了下个的index。根据我的理解就是首先取出当前index的拦截器(interceptors.get(index)),然后将其与下一个连接起来(或者可以理解为去处理下一个index的拦截器)。这里就有了递归调用的感觉
// 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);
我们去看一下Interceptor的intercept方法。可以看到他继续调用了RealInterceptorChain的proceed方法,这就是递归调用了。最后返回经过所有拦截器之后的Response,这就是getResponseWithInterceptorChain() 方法返回的结果。
@Override public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
int followUpCount = 0;
Response priorResponse = null;
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response = null;
boolean releaseConnection = true;
try {
response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
releaseConnection = false;
}
...
}
}
这里应该是使用了责任链模式。
总结下getResonseWithInterceptorChain方法()做的事情。
- 创建一个List(interceptors)用来存储拦截器
- 创建RealInterceptorChain对象,其中传入的包括interceptors与index(为0,从第一个开始)
- 调用RealInterceptorChain对象的proceed方法,在该方法中创建新的RealInterceptorChain对象(next),传入list与index(1)。取出lsit中当前index的拦截器(interceptor),声明Response对象,调用该拦截器的intercept方法,并传入新创建的RealInterceptorChain,并将其返回值付给新创建的Response对象。(Response response = interceptor.intercept(next)),在proceed方法的结束会返回Response对象。
- 拦截器的intercept方法会继续调用传入的RealInterceptorChain对象的proceed方法,这样就会递归处理拦截器链中的下一个拦截器。
- 递归结束后会返回一个Response对象。
3.3同步方式
先回顾一下同步方式是如何请求数据的。
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
我们看到了熟悉的身影。
client.newCall(request)
和异步相同都是先创建一个RealCall对象(实现了接口的newCall方法,运用了工厂模式,不管具体怎么制作,只要一个Call对象。)
@Override public Call newCall(Request request) {
return new RealCall(this, request, false /* for web socket */);
}
继续看execute方法。首先调用了Dispatcher对象的executed方法。
@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);
}
}
Dispatcher的executed方法很简单,就是将请求加入同步进行队列中。
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
之后还是通过getResponseWithInterceptorChain()方法获得Response对象。
到这里,同步方法和异步方法就全部讲解完毕。
4.总结
最后以文字形式总结下总体请求流程,几乎可以当做面试回答去回答面试官了有木有。
首先通过OkHttpClientBuilder创建OkHttpClient实例,之后通过OkHttpClient对象的newCall方法,(传入创建好的Request对象),去创建RealCall对象。
异步方式调用RealCall对象的enqueue方法并传入创建好的Callback对象(创建Callback对象需要重写其OnResponse方法与OnFailure方法分别对应网络请求成功与失败),在该方法下,会继续调用Dispatcher调度器的enqueue方法并传入通过Callback对象创建的AsynCall对象,在该方法中会判断是否立即执行该请求,如果可以就加入异步执行队列,并使用线程池创建一个线程来执行该请求(AsynCall)对象(会调用AsynCall对象的execute方法(不返回Response对象,会根据重定向重试拦截器的isCanceled函数来判断在其中调用OnResponse方法或OnFailure方法))。否则进入等待队列。
同步方式则直接调用RealCall对象的execute方法,在该方法中调用Dispatcher调度器的executed方法(返回Response对象),直接进入同步执行队列。
之后两个execute方法都会调用getResponseWithInterceptorChain方法去获取Response。在getResponseWithInterceptorChain方法中会递归的去处理下一个Interceptor(拦截器),最终返回Response。
本文精髓
1.OkHttpClient实现Call.Factory,负责为Request创建Call(RealCall)。
2.异步方式中加入到执行队列后线程池创建线程并执行该请求会调用AsynCall对象的executed方法,这是因为抽象类NamedRunnable实现了Runnable接口并在run函数中执行execute方法(在类中为抽象方法)而AsynCall类又继承了NamedRunnable类,因此执行请求相当于调用AsynCall对象的execute方法。
3.同步方式调用的是RealCall对象的execute方法,在该方法中首先调用Dispatcher的executed方法,将RealCall对象加入同步执行队列,之后会调用getResponseWithInterceptorChain方法。也就是说同步方式和异步方式调用的最终调用的虽然都是executed方法,但并不是同一个executed方法。网上有的图片直接会都指向同一个executed,难免会让读者疑惑。
4.总结下getResonseWithInterceptorChain方法()做的事情。
- 创建一个List(interceptors)用来存储拦截器
- 创建RealInterceptorChain对象,其中传入的包括interceptors与index(为0,从第一个开始)
- 调用RealInterceptorChain对象的proceed方法,在该方法中创建新的RealInterceptorChain对象(next),传入list与index(1)。取出lsit中当前index的拦截器(interceptor),声明Response对象,调用该拦截器的intercept方法,并传入新创建的RealInterceptorChain,并将其返回值付给新创建的Response对象。(Response response = interceptor.intercept(next)),在proceed方法的结束会返回Response对象。
- 拦截器的intercept方法会继续调用传入的RealInterceptorChain对象的proceed方法,这样就会递归处理拦截器链中的下一个拦截器。
- 递归结束后会返回一个Response对象。
流程图
下图是我总结的总体请求流程,可以说是非常详细了。
参考链接(优秀文章汇总)
1.okhttp源码分析(一)——基本流程(超详细);
2.OkHttp-Request-请求执行流程;
3.Okhttp使用详解
4.Okhttp官方网站
5.拆轮子系列:拆 OkHttp
6.拆轮子:OkHttp 的源码解析(三):任务分发器(Dispatcher)
7.OkHttp Dispatcher的调度过程分析