Retrofit 源码分析5-来了解下 OkHttpCall 的
这期接着上期来看看 retrofit2 包下面的 Call 接口。
An invocation of a Retrofit method that sends a request to a webserver and returns a response.
//这是代码注释,翻译起来可能不能传达其意思,所以直接搬过来了。
里面定义了几个方法,
execute() 表示同步请求,返回值就是网络请求结果
enqueue(Callback<T> callback) 表示异步请求,返回值需要通过 Callback 回调来获取
request() 表示创建一个请求吧,返回 Request 类型
其他方法暂时先不关注。在 HttpServiceMethod 的 invoke 方法里,会创建一个 Call 对象,
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
可见 OkHttpCall 是 Call 的一个实现类,入参之前都有提到过,总的来说入参包含了网络请求会用到的一些资源。看看 OkHttpCall 是如何实现前面说的几个方法的。
execute()
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = getRawCall();
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
主要逻辑有两步,第一步创建 okhttp 包下面的 Call 对象,第二步是通过该对象发起真正的请求并解析返回。考虑到同步请求会造成线程阻塞,所以调用该方法需要放在子线程中进行。
enqueue
@Override
public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch(Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
}
try {
callback.onResponse(OkHttpCall.this, response);
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace();
}
}
});
}
异步方式请求的代码会多些,但本质差别不大,这里可以拆分成三步吧。第一步依然是创建 okhttp 包下面的 Call 对象,并赋值给 rawCall。第二步通过该对象发起异步请求,请求入参是 okhttp 包下面的 Callback 匿名内部类的实现。第三步就是处理回调结果,正确的话就是解析返回值了,错误就回调错误结果。
关于再深入的异步过程后面再看,因为那块就算 OkHttp 的了。
对比上面两个方法,总体思路上是相同的,区别就在于同步,异步处理,实际项目使用上,异步会偏多一些。
request
@Override
public synchronized Request request() {
try {
return getRawCall().request();
} catch (IOException e) {
throw new RuntimeException("Unable to create request.", e);
}
}
通过 okhttp 包下的 Call 对象获取到 Request 对象。
接下来以 enqueue 方法为例,依照前面分析的三步,看下实现细节,
第一步 创建 Call 对象
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
return call;
}
callFactory 的实现类是 OkHttpClient,这个可以通过之前分析得到,requestFactory.create(args) 操作简单理解起来就是拼接请求入参,生成一个 Request 对象,Request 对象里有 url, method, headers, body 这些信息。
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
RealCall 是 okhttp 包下 Call 的实现类,实际上创建的就是这个 RealCall 对象。
第二步 发起异步请求
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
//这个还不知道什么作用,后面可能会去看下
transmitter.callStart();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
OkHttpClient 对象会通过 Dispatcher 对象调用 enqueue 方法,入参是一个 AsyncCall 对象。AsyncCall 是 RealCall 的内部类,继承自 NamedRunnable。后者是个实现了 Runnable 接口的抽象类,大概率会用来做子线程耗时任务。
Dispatcher 类维护着两个队列用于异步请求,同时会通过线程池的方式来创建子线程执行任务,结合上面的 AsyncCall,应该就是会将 AsyncCall 对象放入队列中,接着通过线程池控制线程执行任务。
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
readyAsyncCalls 是个预备队列,AsyncCall 对象首先会添加到这个队列里,另外一个 runningAsyncCalls 队列表示正在执行的队列,
private boolean promoteAndExecute() {
List<AsyncCall> executableCalls = new ArrayList<>();
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
这个方法有两点值得注意,一是限流控制,runningAsyncCalls 队列最大长度为 64,超过之后就不会再入队了,并且同一个 Host 的请求数最大为 5,不然也不给入队。二是 Call 的真正执行,刚才说的 AysncCall 会先被添加到 readyAsyncCalls 里,在这个方法里会遍历队列,如果没达到限流控制就会从 readyAsyncCalls 队列里移除,并添加到 runningAsyncCalls 队列里。接下去就是执行这个 Call 了。
再往下的东西暂时先不看,因为后面涉及线程池,以及再细节的请求执行,内容比较多,后面再另说。
第三步 处理回调结果
处理回调结果主要是一个解析数据的过程,
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
rawResponse = rawResponse
.newBuilder()
.body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
.build();
int code = rawResponse.code();
if (code < 200 || code >= 300) {
ResponseBody bufferedBody = Utils.buffer(rawBody);
return Response.error(bufferedBody, rawResponse);
}
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
}
结合 HTTP 请求状态码,Retrofit 认为 [200, 300) 这个区间的值才是有效的,否则会以错误结束,同时对 204 和 205 做了特殊处理,认为是成功,但不会有返回数据。其他情况就会根据我们实现在 Retrofit 对象里配置的转换器进行数据格式转换。
最终我们所能感知到的就是这个网络请求的成功或者失败。