这是第一次解析源码并把它写出来,在之前,我一直以为只要会用别人的轮子就好,做出实际的效果就行,对看源码对自己的能力提升不以为然。后来偶然听到一句话:看别人的DEMO,你就可以会用轮子,但是要想用好轮子,还是得看源码。我觉得看源码有两个方面的好处:
1. 从本质上去理解所学框架的原理和流程;
2. 可以看到软件开发里常见的思维方法和设计模式;这些在编程里都是相通的。
下面这篇文章,我们队OkHttp框架做一个简单的分析。
使用OkHttp访问网络,通用的做法就是下面四步:
步骤:
//1 创建okHttpClient对象
//2 创建网络Request对象
//3 创建与Request对应的返回Call对象
//4 请求加入调度(直接执行)
在第四步中,访问网络有两种方式,一种是同步请求访问网络。一种是异步请求访问网络,我们先看看同步访问网络是怎么实现的。
同步访问网络实现源码解析:它是通过okHttpClient.newCall(re)创建一个Call对象。如下:
OkHttpClient okHttpClient =new OkHttpClient();
Response response = okHttpClient.newCall(request).execute();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Call call=okHttpClient.newCall(request);
Response response = call.execute();
if(response.isSuccessful()){ Log.i("TAG","response.code()="+response.code());//200 Log.i("TAG","response.body().string()="+response.body().string());
}
这里我们先看看Call对象到底是什么鬼?跟踪源码我们可以看到:
/**
* Prepares the {@code request} to be executed at some point in the future.//准备一个在未来某个点会被执行的网络请求
*/
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
可以看到真正起作用的是RealCall,并非我们看到的Call;我们再看看RealCall是什么?
final class RealCall implements Call {//RealCall是一个继承了Call接口的网络真正请求类,
private final OkHttpClient client;
....
protected RealCall(OkHttpClient client, Request originalRequest) {
this.client = client;
this.originalRequest = originalRequest;
}
//真正执行execute的地方;
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
上面代码中,client.dispatcher().executed(this);是将当前网络请求,从OkHttpClient获得的Dispatcher然后把它加入到分发器里面的队列 executedCalls中,在完成的时候会remove掉,源码如下:
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
上面中的runningSyncCalls是指正在执行的同步任务队列,在OkHttp框架中对网络请求进行分发任务的是Dispatcher类,Dispatcher维护了如下变量,用于控制并发的请求
private int maxRequests = 64;//最大并发请求数为64
private int maxRequestsPerHost = 5;//每个主机最大请求数为5
private ExecutorService executorService;//线程池
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//维护准备执行的异步请求队列,双端队列缓存(用数组实现,可自动扩容,无大小限制);
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在执行的异步任务队列,包括已经取消但是并未finish的请求
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//正在执行的同步任务队列,包括已经取消但是并未finish的请求
Dispatcher: 分发者,也就是生产者(默认在主线程)
AsyncCall: 队列中需要处理的Runnable(包装了异步回调接口)
ExecutorService:消费者池(也就是线程池)
根据生产者消费者模型的模型理论,当入队(enqueue)请求时,如果满足(runningRequests<64 && runningRequestsPerHost<5),那么就直接把AsyncCall直接加到runningCalls的队列中,并在线程池中执行。如果消费者缓存满了,就放入readyAsyncCalls进行缓存等待。