开源库

Retrofit 源码分析5-来了解下 OkHttpCall 的

2021-06-13  本文已影响0人  jkwen

这期接着上期来看看 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 对象里配置的转换器进行数据格式转换。

最终我们所能感知到的就是这个网络请求的成功或者失败。

上一篇 下一篇

猜你喜欢

热点阅读