天天看點

Retrofit2源碼解析——網絡調用流程(下)

Retrofit2源碼解析系列

本文基于Retrofit2的2.4.0版本

implementation 'com.squareup.retrofit2:retrofit:2.4.0'           

上次我們分析到網絡請求是通過OkHttpCall類來完成的,下面我們就來分析下OkHttpCall類。

final class OkHttpCall<T> implements Call<T> {

    ...
    @Override
    public void enqueue(final Callback<T> callback) {
        checkNotNull(callback, "callback == null");

        okhttp3.Call call;
        Throwable failure;

        synchronized (this) {
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;

            call = rawCall;
            failure = creationFailure;
            if (call == null && failure == null) {
                try {
                    //調用createRawCall建立OkHttp3的Call
                    call = rawCall = createRawCall();
                } catch (Throwable t) {
                    throwIfFatal(t);
                    failure = creationFailure = t;
                }
            }
        }

        ...

        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
                Response<T> response;
                try {
                    //解析傳回的結果
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    callFailure(e);
                    return;
                }

                try {
                    callback.onResponse(OkHttpCall.this, response);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }

            @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) {
                    t.printStackTrace();
                }
            }
        });
    }
    
    ...
}           

OkHttpCall的enqueue方法主要幹了2件事,一個是建立OkHttp3的Call用于執行網絡請求;另一個是解析傳回的結果并回調。下面我們來看看建立OkHttp3的Call的過程

//OkHttpCall.class
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}           

可以發現是通過serviceMethod的toCall方法來建立的

//ServiceMethod.class
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
            contentType, hasBody, isFormEncoded, isMultipart);

    ...
    for (int p = 0; p < argumentCount; p++) {
        handlers[p].apply(requestBuilder, args[p]);
    }
    //最後調用OkHttpClient的newCall方法傳回Call
    return callFactory.newCall(requestBuilder.build());
}           

ServiceMethod的toCall方法也是通過OkHttpClient的newCall方法來傳回Call的。

在我們通過OkHttpClient請求得到結果後,我們還需要将傳回的結果Response解析成我們接口需要的實體類型,這就需要用到我們在建立Retrofit時設定的ConverterFactory了,比如GsonConverterFactory。

//OkHttpCall.class
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    ...

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
        //通過serviceMethod的toResponse方法解析
        T body = serviceMethod.toResponse(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        
        catchingBody.throwIfCaught();
        throw e;
    }
}           

OkHttpCall的parseResponse方法調用的是serviceMethod的toResponse方法來解析傳回的結果。

//ServiceMethod.class
R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
}           

在ServiceMethod中最後調用responseConverter的convert方法來轉換傳回的結果。這個responseConverter和上面分析的CallAdapter的确定過程一樣,也是在ServiceMethod的build方法中,通過調用retrofit的requestBodyConverter方法周遊我們傳入的ConverterFactory,直到找到合适的。

//Retrofit.class
public <T> Converter<T, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations, Annotation[] methodAnnotations) {
    return nextRequestBodyConverter(null, type, parameterAnnotations, methodAnnotations);
}

public <T> Converter<T, RequestBody> nextRequestBodyConverter(
        @Nullable Converter.Factory skipPast, Type type, Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations) {
    ...

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
        Converter.Factory factory = converterFactories.get(i);
        Converter<?, RequestBody> converter =
                factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
        if (converter != null) {
            //noinspection unchecked
            return (Converter<T, RequestBody>) converter;
        }
    }

    ...
}           

需要注意的是在建立Retrofit時預設添加了一個BuiltInConverters,這個是Retrofit為我們提供一個預設的responseConverter,它主要處理的是傳回類型是ResponseBody和Void的情況。

final class BuiltInConverters extends Converter.Factory {
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        if (type == ResponseBody.class) {
            return Utils.isAnnotationPresent(annotations, Streaming.class)
                    ? StreamingResponseBodyConverter.INSTANCE
                    : BufferingResponseBodyConverter.INSTANCE;
        }
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
    }
    ...
}           

因為我們一般傳回值類型都是具體的實體類型,是以我們需要添加自己的responseConverter,一般也就是GsonConverterFactory了。

至此,網絡調用的後半部分流程也清楚了:

我們調用Call對象的enqueue方法發起異步請求時,實際上調用的是OkHttpCall對應的enqueue方法。OkHttpCall會先調用ServiceMethod類的toCall方法利用OkHttpClient的newCall方法建立OkHttp3的call對象,然後利用這個call對象執行具體的網絡請求。在網絡請求傳回成功以後會調用ServiceMethod類的toResponse方法利用我們設定的responseConverter将傳回結果轉換成我們需要的類型,然後通過我們設定的回調或是預設的回調方法,将結果回調回主線程,進而完成整個請求過程。

總結

Retrofit2的網絡調用的整個流程我們已經分析完了。通過這次分析,我們可以看到Retrofit2中最主要的就是3個類:Retrofit、ServiceMethod和OkHttpCall。這三個類指責明确,互相配合共同完成整個網絡調用的流程。

(1)Retrofit負責供外部初始化和定制,儲存CallAdapter的清單和ResponseConverterFactory清單。

(2)ServiceMethod對應每一個接口方法的資訊,包括解析注解和參數等,同時它也是連接配接Retrofit和OkHttpCall的橋梁。ServiceMethod中儲存着目前接口對應方法所需要的CallAdapter和ResponseConverter。利用CallAdapter将OkHttpCall轉換成接口需要的類型,供接口調用。利用toResponse方法讓OkHttpCall調用ResponseConverter解析網絡請求傳回的結果。

(3)OkHttpCall則是用來執行具體網絡請求。Retrofit2沒有直接使用OkHttp3的Call接口,而是有自己的Call接口。在OkHttpCall内部通過組合的方法持有OkHttp3的Call接口,并通過ServiceMethod的toCall方法得到OkHttp3的call來進行網絡請求,減少對OkHttp3的耦合。

歡迎關注我的微信公衆号,和我一起每天進步一點點!

繼續閱讀