最近迷上了去研究一些源码,OkHttp在Android项目中广泛应用,应该说Retrofit这个会更广泛些,Retrofit基于OKHttp的RESTful的网络请求的API。其中里面的网络请求就是使用的OKHttp。本文先简单分析下OkHttp,后续在进行分析Retrofit。
我的整体思路就是怎么去阅读OkHttp的源码,理解里面一些比较好的设计方式。本文基于最新的Okhttp,简单介绍下get请求的源码
OkHttp Get请求方式
提供同步和异步请求网络两种方式。
请求方式 | 简单解释 | 是否需要开启线程(请求网络属于耗时操作) |
同步请求 | 执行请求是阻塞式,需要等到HTTP响应之后才会返回。同一时刻只能发起一个请求,synchronized会锁住所有代码,新发起的请求会阻塞,直到当前请求完毕或异常来释放锁。 | 需要在请求的时候,开启子线程,成功之后跳转到UI线程进行渲染UI |
异步请求 | 非阻塞式,通过回调的方式返回到调用者。可以同时发起多个请求,每个请求都会在独立的线程。 | 不需要开启子线程,在OKHttp里面封装一个线程池来处理异步请求,回调方法还在子线程中,不能在回调方法中更新UI。 |
关键类
在进入源码分析之前先大体了解一下涉及到的关键类
关键类 | 实现功能 |
RealCall | 实现了Call接口。用来实现发送请求和得到请求的最后响应 |
具体有以下方法: 1.同步(execute())和异步(enqueue())请求的方法、 2.取消请求(cancel())的方法、 3.是否取消(isExecuted())的方法、是否执行的方法(isCanceled()) 、获取当前request(request())方法 | |
有以下属性: 1.OkHttpClient client:就是在调用的地方创建的OkHttpClient对象,通过构造函数传入到RealCall中 2.boolean executed,用来保证每次到执行请求的方法,在一个线程中只能调用一次 3.Request originalRequest,调用的地方创建的Request 4.HttpEngine 处理request和response对。 | |
Dispatcher | 同步任务的管理和异步任务分发。 |
具体有以下方法: 1. executed(RealCall call):同步任务的请求,最终会将要请求的任务放入到Deque<RealCall> runningSyncCalls队列中。所以同步任务仍需要新建线程来执行该方法。 2. enqueue(AsyncCall call):异步任务的请求。该类里面维持一个线程池,用来进行执行发送的异步任务。所以异步请求不需要新建线程来执行该方法。 3.void cancelAll():取消所有请求,包括同步、异步、以及超出设置的最大数还在排队的异步任务 4. finished():结束同步任务或者异步任务。分别对应的不同的call。 5. void promoteCalls()这个是私有方法,这个是将之前排队的异步请求放入到runningAsyncCalls队列,并执行该异步请求。 | |
有以下属性: 1. maxRequests = 64 :同时能够处理的最大请求数的默认值,也可以通过set方法设置,超出的放入到4中的等待队列中 2.maxRequestsPerHost = 5:每个主机能够同时处理的最大请求数,超出的放入到4中的等待队列中。即 可以一个IP地址下对应的5个请求。 在每次执行异步请求的时候,都会对5集合中的请求数和5集合中对应的host进行比较,超过的话直接放入到4集合(readyAsyncCalls)中排队 3. ExecutorService executorService:用来执行异步任务的线程池,虽然创建的是一个无界的线程池,但受maxRequests和maxRequestsPerHost影响,也不同时处理无界个请求 4. Deque<AsyncCall> readyAsyncCalls:准备执行的异步任务队列 5. Deque<AsyncCall> runningAsyncCalls:正在执行的异步任务队列 6. Deque<RealCall> runningSyncCalls:正在执行的同步任务队列 当然也会有一些上述属性对应的get和set方法。不做解释 | |
AsyncCall | 异步任务的真正执行者。继承于NameRunnable。当调用者调用异步任务的时候,将该call加入到请求队列中 |
具体有以下方法: 1.void execute() :执行异步任务的方法,最终通过回调方法返回到调用地方 2.取消请求(cancel())的方法、 3.获取当前request(request())方法 | |
有以下属性: 1. Callback responseCallback:请求的任务对应的callback。通过构造方法传入 2. boolean forWebSocket:是否是WebSocket。(支持WebSocket) |
Get同步请求
简单示例,具体的使用方式可以参见gitHub的使用文档,我这里简单做源码分析。
//当然你也可以通过建造者模式new OkHttpClient.Builder()....Build()的方式创建client
OkHttpClient client = new OkHttpClient();
//设置request的参数
Request request = new Request.Builder().url("http://www.baidu.com").build();
//创建call,看源码,可以知道这里返回的是RealCall
final Call call = client.newCall(request);
new Thread() {
@Override
public void run() {
try {
//发送请求,得到返回的结果
Response response = call.execute();
Log.d(TAG, "code = " + response.code() + " , msg = " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
插一句:注意这里暂时使用Thread来创建线程,其实更推荐ThreadPoolExecutor来创建线程池。其相关知识点可以自行了解下,我之前也做过一些分析:
进入正文分析:
提供给调用者就是简单的几个方法:
1.创建Request,
2.OkHttpClient根据request来创建Call,从源码中可以看到该得到的Call其实是RealCall——真正的请求执行者
@Override public Call newCall(Request request) {
return new RealCall(this, request);
}
3.调用call里面的execute()方法来将请求发送出去。我们去查看RealCall中的execute()代码
@Override public Response execute() throws IOException {
//synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
try {
//将这次call加入到dispatcher的同步call队列中
client.dispatcher().executed(this);
//得到HTTP的响应,这里是关键点
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException("Canceled");
return result;
} finally {
client.dispatcher().finished(this);
}
}
- synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次,如果多次调用会抛出异常
java.lang.IllegalStateException: Already Executed
at okhttp3.RealCall.execute(RealCall.java:52)
at com.j1.aidl.okhttp.OkhttpActivity$3.run(OkhttpActivity.java:99)
- client.dispatcher().executed(this);将这次call加入到dispatcher的同步call队列中。我们看下dispatcher的源码如下:
/** Running synchronous calls. Includes canceled calls that haven't finished yet.
* 所有的运行中的同步请求 。包括那些还没有结束就被取消的请求(从源码中可以看到调用cancel()取消方法
* 并没有将请求从runningSyncCalls移除,只有调用finish()方法的时候从才会runningSyncCalls移除)
*/
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
/** 将同步请求加入到队列中 */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
- 什么时候才会去发送请求呢,Response result = getResponseWithInterceptorChain(false)这里是关键点,用来得到HTTP的响应。(这个getResponseWithInterceptorChain这个源码,后续我会单独去分析,本文暂时不去过多解释,本文只要知道该方法负责去完成发送请求,得到服务器返回的结果。)
- 最后调用 client.dispatcher().finished(this);将这次请求从队列中移除
/** Used by {@code Call#execute} to signal completion. */
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
}
Get异步请求
简单实例
OkHttpClient client = new OkHttpClient();
//设置request的参数
Request request = new Request.Builder().url("http://www.baidu.com").build();
//创建call,看源码,可以知道这里返回的是RealCall
final 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 {
if (response.isSuccessful()) {
Log.d(TAG, "code = " + response.code() + " , msg = " + response.body().string());
}
}
});
1和2同同步操作,直接看第3步
3.同样调用RealCall中的execute()方法,查看源码如下:
void enqueue(Callback responseCallback, boolean forWebSocket) {
//同样synchronized来保证每次线程只调用该方法一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//执行AsyncCall里的run方法
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
- synchronized关键字用来保证该方法是在同一线程中该方法只能调用一次,如果多次调用会抛出异常
- 调用client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));来执行AsyncCall的run方法。因为可以看到在dispatcher里面维护的一个线程池。该方法里面将AsyncCall加到线程池里面,执行AsyncCall的run方法。源码如下:
synchronized void enqueue(AsyncCall call) {
//首先要判断运行异步任务队列中是否已满,如果没有满的话,则执行run方法;如果已满,只能加入到等待队列中等待
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
- 那么请求异步任务的重任就落到了AsyncCall身上了,该类定义在RealCall中的内部类,其执行异步任务的execute()方法的源码如下:
@Override protected void execute() {
boolean signalledCallback = false;
try {
//通过该方法获取到response,同同步任务一样
Response response = getResponseWithInterceptorChain(forWebSocket);
//不同的是,通过回调的方式返回
if (canceled) {
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 {
//将该异步任务从队列中移除,同时还会对readyAsyncCalls队列中判断是否有还没有执行的异步任务
client.dispatcher().finished(this);
}
}
- 过程和同步类似,都是通过getResponseWithInterceptorChain获取到response,将结果通过回调方式返回,最后调用client.dispatcher().finished(this);将这次请求从队列中移除,finished()源码如下:
synchronized void finished(AsyncCall call) {
if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
promoteCalls();
}
- 同时在finished()方法中还要检测readyAsyncCalls队列中是否有待执行的异步任务,如果有的话,执行异步任务,并将异步任务放到runningAsyncCalls集合中。promoteCalls()源码如下:
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> 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.
}
}
总结
整理完这篇文档之后,发现自己对OKHttp又有了新的认识,后续我要分析下RealCall中的 getResponseWithInterceptorChain()方法了,希望自己有所进步