一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpClient。
我将通过一系列的文章来分析okhttp的源码:
在app的build.gradle文件中
dependencies {
compile ‘com.squareup.okhttp3:okhttp:3.10.0’
}
这里引入的3.10.0版本,该系列文章分析的源码均基于这个版本。
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://xxx").get().build();
Call call = client.newCall(request);
try {
call.execute();
} catch (IOException e) {
e.printStackTrace();
}
OkHttpClient client = new OkHttpClient.Builder().build();
Request request = new Request.Builder().url("http://xxx").get().build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException{
}
});
通过上面同步和异步get请求的使用方法,大致可以发现okhttp暴露给上层开发者使用的主要类OkHttpClient、Request和Call,当然不仅仅只是这些接下来我们就对这3个类进行分析
OkHttpClient client = new OkHttpClient.Builder().build();
OkHttpClient.Builder, OkHttpClient的内部类,相信见到这个Builder大家都不会陌生,这种构建对象的方式就是设计模式中的建造者模式,当构建一个对象参数比较多,并且根据不同的参数实现不同的对象特征的时候就会使用这种模式。创建过程不是我们分析的重点,感兴趣的小伙伴可以去源码里看一下。看过我文章的都知道我的源码分析都是配合我的灵魂画技来展示的,下面就用UML类图的方式来表现OkhttpClient类中比较重要的部分
OkHttpClient中比较重要的成员变量和方法都在这个图中了,其中重要成员变量dispatcher,重要方法Call newCall(Request request)我们会在稍后的部分进行分析。
Request request = new Request.Builder().url("http://xxxx").get().build();
Request对象的创建跟OkHttpClient一样都是建造者模式。Request对象内部就简单的多了,就是封装了请求需要的url、方法等。
Call call = client.newCall(request);
Call对象的创建是调用的OkHttpClient对象的newCall方法,传入一个Request对象参数。至此我们的三个对象就关联起来了,接下来我们看一下OkHttpClient中的newCall方法。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
newCall方法的返回值类型是一个Call,需要的入参是一个Request对象。
这里调用了RealCall类中静态方法newRealCall(),newRealCall()的返回值类型是一个Call。接下来我们到RealCall类中看一看具体实现
RealClall
//Call接口的实现类
final class RealCall implements Call {
//OkHttpClient对象
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
private EventListener eventListener;
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);
}
//静态方法,内部通过构造函数创建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.eventListener = client.eventListenerFactory().create(call);
return call;
}
}
RealCall类中静态方法newRealCall()的返回值类型是一个RealCall,内部通过构造函数构建了一个RealCall对象,并且我们发现Call原来是一个接口,而RealCall对象是Call的实现类。OkhttpClient对象调用newCall(request)方法实际获得的是Call接口的实现类RealCall的对象。到这里我们的3个类的创建过程都清晰了。
接下来我们简单看一下RealCall类。
RealCall.AsyncCall
RealCall中有一个内部类AsyncCall,AsyncCall从名字就可以看出来和异步相关,而AsyncCall中就有一个很重要的成员变量responseCallback,就是我们网络请求的回调接口Callback,AsyncCall继承自NamedRunnable,而NamedRunnable是抽象类,并且是Runnable接口的实现类,那么我从这层关系就可以知道RealCall的内部类AsyncCall是一个线程类。好了暂时先看到这里,这个AsyncCall后面会再次提到,当然RealCall中还有一些很重要的成员变量和方法,这些都交给我这个灵魂画手吧~!
以上就是RealCall。
OK到此我们把OkhttpClient、Request和Call都分析了一下,这里对于创建过程做一个总结:
下面就是灵魂画手最激动人心的时刻,将这三者的关系通过UML类图表现出来。出来吧叼毛兽!
得到了Call对象我们就可以用这个对象来执行同步请求或者异步请求了,从上面的分析来看Call是一个接口,那么我们就去它的实现类RealCall中去看:
接下来就来分别分析上面的提到的两个方法。
@Override public Response execute() throws IOException {
synchronized (this) {
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);
}
}
以上就是execute()方法的内部逻辑,我们将上面代码拆开分析,这里只分析主干线,第一个if判断
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
表示请求只能执行一次,不然我分分钟钟抛个异常给你看。然后我们看try{}部分
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
}
代码也很简单,try的逻辑也很简单,主要的就是上面两行代码,这里分别执行了client.dispatcher().executed(this)方法和getResponseWithInterceptorChain()方法。
getResponseWithInterceptorChain() 这个方法我们后续篇章会单独去讲解它,这里面是okhttp中很重要的一个概念 【拦截器】。
我们再来看finally{}部分。
finally {
client.dispatcher().finished(this);
}
可以发现无论是try部分还是最终的finally部分都出现了client.dispatcher(),都是调用client.dispatcher()所返回的对象的相应方法来进行处理,client我们自然不陌生就是OkHttpClient的对象。那么它的dispatcher()方法返回了什么呢? 还记得我们OkHttpClient的UML类图吗?不记得了我们可以翻回前面看一下,上文有提到OkHttpClient中的重要属性和方法,前面newCall()方法我们已经分析过了,剩下的就是重要成员变量dispatcher,而client.dispatcher()返回的就是Dispatcher类在OkHttpClient中的成员变量dispatcher。
OkhttpClient中的 **dispatcher()**方法
public Dispatcher dispatcher() {
return dispatcher;
}
方法很简单,就是返回Dispatcher类的实例对象,到这里我们的同步请求就把逻辑引入到Dispatcher类中。我们先不去看这个类,再回过头来看看Call的异步请求!
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
与同步请求相同的if判断,这里不在赘述,关键代码就是最后一行
client.dispatcher().enqueue(new AsyncCall(responseCallback));
同样的异步请求也将逻辑引入到Dispatcher类中。
那么接下来我们就分析Dispatcher类,看看这个类在okhttp中是充当了什么样的角色,同步和异步的请求都在这个类中做了些什么。
这次我们先上图
Dispatcher的创建离不开OkHttpClient,Dispatcher类是作为OkHttpClient对象的成员变量dispatcher 存在的,OkHttpClient在通过Builder()方式创建对象的时候就构建了Dispatcher对象,为成员变量dispatcher赋值。这里我们截取部分OkHttpClient的创建过程。
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
......//省略部分代码
}
接下来深入Dispathcer类,其中重要的成员变量和方法都在上面的图中体现了,可以看到Dispathcer中有线程池对象,还有3个队列,以及同步请求和异步请求在RealCall中出现的方法execute()和enqueue(Callback responseCallback)。
其实从成员变量上我们大致可以看出这个类的重要性,线程池、队列等等,我们都知道Android中网络请求都是在子线程中进行,频繁的创建新线程是很耗费资源的事情,一般需要频繁创建线程的情况,我们都是使用线程池来管理线程的创建和销毁,所以我能想到的就是Dispathcer类内部维护了线程池,管理请求任务队列,调度执行请求任务。那么是不是这样的呢?我们从Dispathcer源码中结合UML图来找答案。
Dispathcer成员变量
//最大并发请求数为64
private int maxRequests = 64;
//每个主机最大请求数为5
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** 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) {
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);
}
同步请求逻辑很简单,就是将RealCall 加入到同步请求队列。
异步请求:
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
异步比同步复杂一些,判断了队列的长度是否小于maxRequests (64) 并且当前主机执行请求的数是否小于maxRequestsPerHost(5)。
true:
加入到正在执行的异步请求队列,调用线程池执行AsyncCall ,还记得AsyncCall 吧,它是RealCall的内部类,是一个线程类。
false:
加入到准备执行的异步请求队列中
因为线程池调用的是AsyncCall这个线程类,这里我们就要跳到RealCall的内部类AsyncCall中去看相应的方法。我们知道AsyncCall既然是线程类一定有run方法,我们在讲RealCall的时候,通过UML类图可以发现AsyncCall继承自NamedRunnable这个抽象类,NamedRunnable的run方法中执行了execute()方法,由于execute()方法是一个抽象方法,那么转了一圈我们又回到了AsyncCall类中来看具体实现。
@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);
}
}
同样的看try{}部分
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
}
getResponseWithInterceptorChain() 这个重要的方法也出现在了异步请求中,我们这里还是跳过这个方法,继续向下看
finally部分
finally {
client.dispatcher().finished(this);
}
可以发现同步和异步其实finally 部分是一样的,都涉及到了Dispatcher类,都是调用了它的finished方法,下面我将同步和异步中调用的finished方法一起列出来,方便观看和比较。
同步和异步finally中的重要方法
//异步请求最终调用
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
//同步请求最终调用
/** Used by {@code Call#execute} to signal completion. */
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!");
//根据传入的promoteCalls判断是否执行promoteCalls()方法,
//同步传入的false,异步传入的true
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
//异步请求的promoteCalls为true才会调用到该方法
private void promoteCalls() {
//已经达到最大并发数直接return
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
//没有准备执行的任务直接return
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
//循环遍历准备执行的队列
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
//将请求任务从正在准备执行的队列中移除
i.remove();
//放入正在执行的队列中
runningAsyncCalls.add(call);
//调用线程池执行请求任务
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
无论是同步还是异步请求,请求完成后调用了finished从队列中移除call请求任务。异步方法还会执行promoteCalls()方法,循环从readyAsyncCalls准备执行任务队列中获取Call对象加入到runningAsyncCalls正在执行任务队列中。
到这里我们就可以对Dispatcher进行一个分析总结了,Dispatcher就像它的英文翻译一样调度器,无论是同步还是异步都是通过RealCall调用Dispatcher这个okhttp的分发调度器通过线程池来执行具体的请求任务。请求完成后又将任务从队列中清除。
接下来又到的激动人心的时刻,叼毛兽升级~!
Dispatcher总结:
发送的同步和异步请求都会在Dispatcher中进行状态的管理,维护请求状态,并且内部维护一个线程池用于执行请求。
到此我们的这一篇源码分析就基本完事了,到现在为止我们只是分析了一部分的执行过程,真正的网络请求部分我还没有涉及到,而这些都是在同步和异步请求中都出现的代码
Response response = getResponseWithInterceptorChain();
中体现,我们将在下一篇中进行分析,来讲一讲okhttp的拦截器。敬请期待