Android 網絡編程:Retrofit源碼解析

版權聲明:本文爲博主原創文章,未經博主允許不得轉載
文章分類:Android知識體系 - 網絡編程

前言

Retrofit是一個基於OkHttp、遵循RESTful API設計風格的網絡請求封裝框架,本文將按照其工作流程逐步分析對應的源碼(本文使用的Retrofit版本爲2.5.0)


源碼分析

1. 請求示例

以下是一次簡單的請求示例,首先我們需要定義一個接口API,並使用註解描述其中的API方法

public interface ExpressService {
    @GET("query")
    Call<ResponseBody> get(@Query("type") String type, @Query("postid") String postid);
}

然後是Retrofit的工作流,可以分爲三步:

  1. 構建Retrofit對象
  2. 加載API方法配置,生成請求執行器Call
  3. 使用Call對象執行請求,處理響應數據
public void asyncGet() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://www.kuaidi100.com/")
            .build();

    ExpressService expressService = retrofit.create(ExpressService.class);
    Call<ResponseBody> call = expressService.get("ems", "11111111");

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {

        }
    });
}

下面我們將按照上述過程,分析對應的源碼

2. 構建Retrofit對象

本章我們將分析Retrofit對象構建的過程,以下是對應的示例代碼

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://www.kuaidi100.com/")
        .build();

2.1 Retrofit類的成員變量

首先來看Retrofit類聲明瞭哪些成員變量

public final class Retrofit {
    private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

    final okhttp3.Call.Factory callFactory;
    final HttpUrl baseUrl;
    final List<Converter.Factory> converterFactories;
    final List<CallAdapter.Factory> callAdapterFactories;
    final @Nullable Executor callbackExecutor;
    final boolean validateEagerly;

    ...
}
  • serviceMethodCache:serviceMethodCache是一個ConcurrentHashMap類型的Map集合,因此它支持併發操作且線程安全,其存儲對象是ServiceMethod。ServiceMethod我們可以理解是API方法的配置器以及代理中轉站,具體內容會在後文進行分析

  • callFactory:callFactory就是生產請求執行器的工廠(Call.Factory),Retrofit中默認的請求執行器工廠是OkHttpClient。如果我們沒有設置自定義的請求執行器工廠,那麼就會在構建Retrofit對象的過程中爲我們創建一個OkHttpClient實例

  • baseUrl:API接口基地址的封裝對象,類型爲HttpUrl

  • converterFactories數據轉換器工廠(Converter.Factory)的集合,該工廠的產品數據轉換器(Converter)作用是對請求與響應數據進行序列化和反序列化

  • callAdapterFactories請求適配器工廠(CallAdapter.Factory)的集合,該工廠的產品請求適配器(CallAdapter)用於改變執行請求的方式,例如我們可以通過添加支持RxJava的請求適配器,將默認執行請求的方式改爲RxJava調用鏈的方式

  • callbackExecutor回調執行器,主要作用是處理請求回調,例如將回調所在線程從子線程切換至主線程

  • validateEagerly是否提前加載API方法配置的標誌位

2.2 構建Builder

Retrofit對象通過建造者模式進行構建,我們來看下Builder是如何初始化的

public static final class Builder {
    private final Platform platform;
    ...

    Builder(Platform platform) {
        this.platform = platform;
    }

    public Builder() {
        this(Platform.get());
    }
    ...
}

我們看見Builder的構造方法需要傳入Platform對象,它的主要作用是根據運行平臺爲Retrofit提供默認的配置方法、工廠類或者工廠類的集合。Platform可以通過Platform.get()方法獲取實例,我們來看下方法相關源碼

class Platform {
    private static final Platform PLATFORM = findPlatform();

    static Platform get() {
        return PLATFORM;
    }

    private static Platform findPlatform() {
        try {
            Class.forName("android.os.Build");
            if (Build.VERSION.SDK_INT != 0) {
                return new Android();
            }
        } catch (ClassNotFoundException ignored) {
        }
        try {
            Class.forName("java.util.Optional");
            return new Java8();
        } catch (ClassNotFoundException ignored) {
        }
        return new Platform();
    }
    ...
}

調用Platform.get()方法獲取的是靜態成員變量PLATFORM,PLATFORM則通過findPlatform()方法生成

findPlatform()主要通過是否能查找到指定的類來判斷當前Retrofit運行的平臺,如果是運行在Android平臺,則返回Platform.Android的實例;如果是Java平臺,則返回Platform.Java8的實例;如果兩者皆非,則返回Platform自身的實例

現在我們是在Android平臺下運行Retrofit,所以繼續往下看Platform.Android的代碼

// Platform
static class Android extends Platform {
    @IgnoreJRERequirement // Guarded by API check.
    @Override
    boolean isDefaultMethod(Method method) {
        if (Build.VERSION.SDK_INT < 24) {
            return false;
        }
        return method.isDefault();
    }

    @Override
    public Executor defaultCallbackExecutor() {
        return new MainThreadExecutor();
    }

    @Override
    List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
            @Nullable Executor callbackExecutor) {
        if (callbackExecutor == null) throw new AssertionError();
        ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
        return Build.VERSION.SDK_INT >= 24
                ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
                : singletonList(executorFactory);
    }

    @Override
    int defaultCallAdapterFactoriesSize() {
        return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
    }

    @Override
    List<? extends Converter.Factory> defaultConverterFactories() {
        return Build.VERSION.SDK_INT >= 24
                ? singletonList(OptionalConverterFactory.INSTANCE)
                : Collections.<Converter.Factory>emptyList();
    }

    @Override
    int defaultConverterFactoriesSize() {
        return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
    }

    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(Runnable r) {
            handler.post(r);
        }
    }
}

Platform.Android中主要定義了以下內容:

  • 默認的回調執行器MainThreadExecutor,負責將請求回調的運行線程切換爲主線程
  • 默認的請求適配器工廠集合,當Android版本大於等於24時,集合依次添加了兩個適配器工廠CompletableFutureCallAdapterFactoryExecutorCallAdapterFactory;當Android版本小於24時,則爲只存儲了ExecutorCallAdapterFactory的單元素集合
  • 默認的數據轉換器工廠集合,當Android版本大於等於24時,返回的是只儲存了OptionalConverterFactory的單元素集合;當系統版本小於24時,返回的是一個空集合

以上平臺默認的內容都會在Builder調用build()完成構建時用到,具體的我們待會再細說,現在先來看下Builder主要提供了哪些方法

// Retrofit.Builder
public Builder client(OkHttpClient client) {
    return callFactory(checkNotNull(client, "client == null"));
}

public Builder callFactory(okhttp3.Call.Factory factory) {
    this.callFactory = checkNotNull(factory, "factory == null");
    return this;
}

public Builder baseUrl(String baseUrl) {
    checkNotNull(baseUrl, "baseUrl == null");
    return baseUrl(HttpUrl.get(baseUrl));
}

public Builder addConverterFactory(Converter.Factory factory) {
    converterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
}

public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
    callAdapterFactories.add(checkNotNull(factory, "factory == null"));
    return this;
}

public Builder callbackExecutor(Executor executor) {
    this.callbackExecutor = checkNotNull(executor, "executor == null");
    return this;
}

public Builder validateEagerly(boolean validateEagerly) {
    this.validateEagerly = validateEagerly;
    return this;
}

簡單介紹一下這些方法的作用

  • client():添加自定義配置的OkHttpClient
  • callFactory():添加自定義的請求執行器工廠
  • baseUrl():添加API接口的基地址
  • addConverterFactory():添加自定義的數據轉換器
  • addCallAdapterFactory():添加自定義的請求適配器
  • callbackExecutor():添加自定義的回調執行器
  • validateEagerly():設置是否預加載API方法的標誌位

完成Retrofit的構建最終需要調用Builder.build()方法,來看下源碼

// Retrofit.Builder
public Retrofit build() {
    if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    }

    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        callFactory = new OkHttpClient();
    }

    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
    }

    // Make a defensive copy of the adapters and add the default Call adapter.
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    // Make a defensive copy of the converters.
    List<Converter.Factory> converterFactories = new ArrayList<>(
            1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(platform.defaultConverterFactories());

    return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
            unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}

build()方法完成了以下工作:

  • 當用戶沒有添加自定義的CallFactory時默認使用OkHttpClient
  • 當用戶沒有添加自定義的CallbackExecutor時默認使用Platform.defaultCallbackExecutor(),即MainThreadExecutor
  • callAdapterFactories創建一個保護性拷貝,然後將Platform裏定義的所有請求適配器添加進來
  • converterFactories創建了一個保護性拷貝,然後依次將內置的轉換器工廠BuiltInConverters、所有自定義的轉換器工廠、Platform裏定義的所有轉換器工廠添加進來

完成上述工作後將相關參數傳入Retrofit的構造方法中即完成構建Retrofit對象的工作。其中需要注意的是converterFactories和callAdapterFactories都變成了不可修改的List集合,這意味着後續我們不可以再更改這兩個集合中的內容了

至此Retrofit對象構建的過程就分析完了,下一章我們將分析API接口方法加載配置以及轉換爲請求執行器的過程

2.3 本章小結

這一章我們分析了構建Retrofit對象的過程,過程中主要完成了以下工作:

  • 創建內置的成員實例以及工廠集合,用來維持基礎的功能
  • 通過構建方法保存用戶自定義的內容,例如自定義的數據轉換器、請求適配器、回調執行器等等,用來運行擴展的功能

3. 加載API方法配置

本章我們將分析API方法加載配置的過程,以下是對應的示例代碼

// 請求示例
ExpressService expressService = retrofit.create(ExpressService.class);
Call<ResponseBody> call = expressService.get("ems", "11111111");

Retrofit對象構建完畢後,下一步是通過Retrofit.create(Class<T> service)方法實現API接口,該方法的代碼如下

// Retrofit
public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
        eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
            new InvocationHandler() {
                private final Platform platform = Platform.get();
                private final Object[] emptyArgs = new Object[0];

                @Override
                public @Nullable
                Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
                    // If the method is a method from Object then defer to normal invocation.
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    if (platform.isDefaultMethod(method)) {
                        return platform.invokeDefaultMethod(method, service, proxy, args);
                    }
                    return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
                }
            });
}

該方法主要是通過運用動態代理的方式爲請求接口生成一個代理對象,我們對接口所有方法的調用都會轉發到代理對象中。現在我們一步步分析是如何完成整個動態代理的過程的

3.1 API方法的校驗和預加載

首先是調用Utils.validateServiceInterface(Class<T> service)對接口類進行校驗

// Utils
static <T> void validateServiceInterface(Class<T> service) {
    if (!service.isInterface()) {
        throw new IllegalArgumentException("API declarations must be interfaces.");
    }
    // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
    // Android (http://b.android.com/58753) but it forces composition of API declarations which is
    // the recommended pattern.
    if (service.getInterfaces().length > 0) {
        throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
}

這裏規定我們傳入的參數必須是一個接口類,並且該接口不能繼承其他的接口

回到create方法中,接下來會通過標誌位validateEagerly來決定是否提前爲API方法加載相應的配置

// Retrofit.create
if (validateEagerly) {
    eagerlyValidateMethods(service);
}

我們知道動態代理是在接口方法被調用時纔會生效的,這類似於懶加載策略,Retrofit默認採用的就是這種方式,而我們可以通過Retrofit.Builder.validateEagerly()方法將validateEagerly標誌設置爲true,Retrofit就會調用eagerlyValidateMethods()提前爲接口方法加載配置

eagerlyValidateMethods()源碼如下

// Retrofit
private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
        if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
            loadServiceMethod(method);
        }
    }
}

具體邏輯爲遍歷接口中的方法(method),然後判斷方法是否爲默認方法靜態方法(接口的默認方法和靜態方法都是Java8新增的特性),若不是則調用loadServiceMethod()爲接口方法加載相應的配置。loadServiceMethod()方法的邏輯我們待會再細說,現在繼續分析Retrofit.create()方法

經過預加載的邏輯後,下一步就是執行動態代理相關的邏輯

3.2 動態代理中的校驗

// Retrofit.create
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
        new InvocationHandler() {
            private final Platform platform = Platform.get();
            private final Object[] emptyArgs = new Object[0];

            @Override
            public @Nullable
            Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                    return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                    return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
            }
        });

首先依然是對接口方法的校驗,會判斷此次調用的方法是否爲Object對象的方法(非接口方法),若是則正常調用,不進行任何代理操作。然後判斷該方法是否爲默認方法,若是則調用Platform對象提供的配置方法invokeDefaultMethod()並返回。invokeDefaultMethod()在Android平臺下會拋出UnsupportedOperationException異常,具體代碼如下

// Platform
@Nullable
Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object,
                           @Nullable Object... args) throws Throwable {
    throw new UnsupportedOperationException();
}

執行完所有的校驗工序之後,最終依然是調用loadServiceMethod()開始加載API方法的配置,我們來看下源碼

ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
        result = serviceMethodCache.get(method);
        if (result == null) {
            result = ServiceMethod.parseAnnotations(this, method);
            serviceMethodCache.put(method, result);
        }
    }
    return result;
}

方法的邏輯非常簡單,功能可以分爲兩部分來看

  • 使用retrofit2.ServiceMethod處理接口方法
  • 將處理後的結果緩存至serviceMethodCache中,這樣下次再調用該接口方法時就無需重複處理了

繼續往下看ServiceMethod的代碼

abstract class ServiceMethod<T> {
    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

        Type returnType = method.getGenericReturnType();
        if (Utils.hasUnresolvableType(returnType)) {
            throw methodError(method,
                    "Method return type must not include a type variable or wildcard: %s", returnType);
        }
        if (returnType == void.class) {
            throw methodError(method, "Service methods cannot return void.");
        }

        return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    }

    abstract @Nullable T invoke(Object[] args);
}

ServiceMethod是一個抽象類,它只有兩個方法,一個是invoke(),這是一個抽象方法,具體的邏輯由ServiceMethod的子類實現,當調用接口方法時會被動態代理到這個方法中

另一個方法是parseAnnotations(),這是一個靜態方法,它的功能如下

  • 創建RequestFactory實例,具體方式爲調用RequestFactory.parseAnnotations()方法
  • 校驗接口方法的返回類型。Retrofit會判斷該返回類型是否屬於無法處理的類型(包含類型變量、通配符的返回類型以及void類型),若接收到這些返回類型時會直接拋出異常
  • 繼續調用HttpServiceMethod.parseAnnotations()完成接口方法後續的配置加載工作

這一部分最主要的關注點是RequestFactory。RequestFactory是請求體對象(okhttp3.Request)的工廠類,我們可以調用RequestFactory.create()創建一個請求體的實例,下面我們就來詳細分析生成RequestFactory的過程

3.3 生成RequestFactory

以下是與RequestFactory.parseAnnotations()方法相關的代碼

final class RequestFactory {
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new Builder(retrofit, method).build();
    }
    ...
    RequestFactory(Builder builder) {
        method = builder.method;
        baseUrl = builder.retrofit.baseUrl;
        httpMethod = builder.httpMethod;
        relativeUrl = builder.relativeUrl;
        headers = builder.headers;
        contentType = builder.contentType;
        hasBody = builder.hasBody;
        isFormEncoded = builder.isFormEncoded;
        isMultipart = builder.isMultipart;
        parameterHandlers = builder.parameterHandlers;
        isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
    }
    ...
    static final class Builder {
        ...
        Builder(Retrofit retrofit, Method method) {
            this.retrofit = retrofit;
            this.method = method;
            this.methodAnnotations = method.getAnnotations();
            this.parameterTypes = method.getGenericParameterTypes();
            this.parameterAnnotationsArray = method.getParameterAnnotations();
        }

        RequestFactory build() {
            for (Annotation annotation : methodAnnotations) {
                parseMethodAnnotation(annotation);
            }

            if (httpMethod == null) {
                throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
            }

            if (!hasBody) {
                if (isMultipart) {
                    throw methodError(method,
                            "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
                }
                if (isFormEncoded) {
                    throw methodError(method, "FormUrlEncoded can only be specified on HTTP methods with "
                            + "request body (e.g., @POST).");
                }
            }

            int parameterCount = parameterAnnotationsArray.length;
            parameterHandlers = new ParameterHandler<?>[parameterCount];
            for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
                parameterHandlers[p] =
                        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
            }

            if (relativeUrl == null && !gotUrl) {
                throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
            }
            if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
                throw methodError(method, "Non-body HTTP method cannot contain @Body.");
            }
            if (isFormEncoded && !gotField) {
                throw methodError(method, "Form-encoded method must contain at least one @Field.");
            }
            if (isMultipart && !gotPart) {
                throw methodError(method, "Multipart method must contain at least one @Part.");
            }

            return new RequestFactory(this);
        }

        ...
    }
}

RequestFactory同樣通過建造者模式來構建實例,我們可以看到Builder.build()方法中有許多狀態位的校驗邏輯,主要作用是當用戶在創建請求接口中錯誤地使用註解或配置參數時,可以拋出相應的異常告知用戶。這部分的細節就不一一查看了,感興趣的同學可以自行研究

這裏我們最主要關注的地方有兩點:一是遍歷接口方法的註解,然後通過Builder.parseMethodAnnotation()方法解析註解的過程;二是遍歷方法的參數,然後通過Builder.parseParameter()方法解析參數的過程

3.3.1 解析方法註解

首先來看第一點,parseMethodAnnotation()的代碼如下

// RequestFactory.Builder
private void parseMethodAnnotation(Annotation annotation) {
    if (annotation instanceof DELETE) {
        parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
    } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
    } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
    } else if (annotation instanceof PATCH) {
        parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
    } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
    } else if (annotation instanceof PUT) {
        parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
    } else if (annotation instanceof OPTIONS) {
        parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
    } else if (annotation instanceof HTTP) {
        HTTP http = (HTTP) annotation;
        parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
    } else if (annotation instanceof retrofit2.http.Headers) {
        String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
        if (headersToParse.length == 0) {
            throw methodError(method, "@Headers annotation is empty.");
        }
        headers = parseHeaders(headersToParse);
    } else if (annotation instanceof Multipart) {
        if (isFormEncoded) {
            throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isMultipart = true;
    } else if (annotation instanceof FormUrlEncoded) {
        if (isMultipart) {
            throw methodError(method, "Only one encoding annotation is allowed.");
        }
        isFormEncoded = true;
    }
}

這裏有大量的判斷分支語句,我們可以按照註解的功能分成三類來看。第一類註解主要是描述HTTP方法的,例如@DELETE@GET@POST等,解析方法爲parseHttpMethodAndPath(),來看下源碼

// RequestFactory.Builder
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    if (this.httpMethod != null) {
        throw methodError(method, "Only one HTTP method is allowed. Found: %s and %s.",
                this.httpMethod, httpMethod);
    }
    this.httpMethod = httpMethod;
    this.hasBody = hasBody;

    if (value.isEmpty()) {
        return;
    }

    // Get the relative URL path and existing query string, if present.
    int question = value.indexOf('?');
    if (question != -1 && question < value.length() - 1) {
        // Ensure the query string does not have any named parameters.
        String queryParams = value.substring(question + 1);
        Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
        if (queryParamMatcher.find()) {
            throw methodError(method, "URL query string \"%s\" must not have replace block. "
                    + "For dynamic query parameters use @Query.", queryParams);
        }
    }

    this.relativeUrl = value;
    this.relativeUrlParamNames = parsePathParameters(value);
}

這個方法有三個參數

  • httpMethod:表示描述HTTP方法的註解的名稱
  • value:註解包含的內容,例如@GET("query?type=ems&postid=11111111")中括號裏的內容實際上就是指服務端接口Url的資源路徑和查詢參數部分。這些內容可以通過調用這一系列註解的value()方法獲取,這是在定義註解時設置的
  • hasBody:該HTTP方法是否攜帶請求正文數據

知道了每個參數的含義,後面的代碼就好理解了,流程是這樣的:首先會對一個接口方法是否同時設置了多個HTTP方法註解進行了校驗;然後校驗value是否爲空,爲空就不需要繼續解析了,因爲這部分內容不是在方法註解裏設置就是在方法參數中設置,這裏沒有,那就直接交給後面解析參數時再去處理;若value不爲空,則校驗查詢參數部分是否符合要求,符合要求則繼續解析查詢參數字符串得到參數集合,不符合則拋出異常,這些操作主要通過Java正則解析相關的Matcher類完成的,就不細說了

回到RequestFactory.Builder.parseMethodAnnotation(),我們來看第二類註解。第二類註解只有@Headers一個,負責設置請求頭信息,解析的方法爲Builder.parseHeaders(),這部分內容比較簡單,就不展開了

第三類註解主要負責描述請求報文數據的類型,有@Multipart@FormUrlEncoded兩種,但因爲這兩種類型都需要配合方法參數的註解使用,所以這裏的處理過程只是簡單地校驗兩者不重複設置就行

實際上方法註解中還有第四類,那就是@Streaming,這是用於描述響應正文數據的,因此Retrofit將它的處理過程放到了後面配置響應內容相關的部分再進行

至此解析方法註解的部分我們就分析完了,接下來看解析參數的部分

3.3.2 解析方法參數

解析方法參數對應的方法是Builder.parseParameter(),代碼如下

// RequestFactory.Builder
private @Nullable ParameterHandler<?> parseParameter(
        int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
    ParameterHandler<?> result = null;
    if (annotations != null) {
        for (Annotation annotation : annotations) {
            ParameterHandler<?> annotationAction =
                    parseParameterAnnotation(p, parameterType, annotations, annotation);

            if (annotationAction == null) {
                continue;
            }

            if (result != null) {
                throw parameterError(method, p,
                        "Multiple Retrofit annotations found, only one allowed.");
            }

            result = annotationAction;
        }
    }
    ...
    return result;
}

這裏的核心邏輯是通過parseParameterAnnotation()方法解析參數註解生成ParameterHandler對象,同時規定一個參數只能設置一個Retrofit定義的註解

ParameterHandler是一個抽象類,它的子類封裝了參數的數據和數據的處理過程,並且和不同註解類型的參數一一對應,例如子類ParameterHandler.Query封裝了@Query註解的參數,ParameterHandler.Field則封裝了@Field註解的參數

作爲參數的處理器,ParameterHandler可以在構建請求體對象時利用專屬的參數數據轉換器將數據轉換成請求需要的格式,這裏我們以ParameterHandler.Query爲例進行分析

// ParameterHandler
static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
        this.name = checkNotNull(name, "name == null");
        this.valueConverter = valueConverter;
        this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
        if (value == null) return; // Skip null values.

        String queryValue = valueConverter.convert(value);
        if (queryValue == null) return; // Skip converted but null values

        builder.addQueryParam(name, queryValue, encoded);
    }
}

ParameterHandler.Query有三個成員屬性,其中name和encoded對應了@Query定義的屬性

@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {
  /** The query parameter name. */
  String value();

  /**
   * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded.
   */
  boolean encoded() default false;
}

而valueConverter指的就是參數數據轉換器,它可以通過調用convert()方法進行數據轉換。參數處理的過程都放在Query.apply()方法中,當構建請求體需要此參數的數據時,就會調用apply()方法,然後通過傳入的RequestBuilder引用設置數據

ParameterHandler的分析就到這,其他參數處理器就不一一分析了,因爲套路基本上都是一樣的,現在我們繼續分析parseParameterAnnotation()方法是如何生成ParameterHandler的

// RequestFactory.Builder
@Nullable
private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
    if (annotation instanceof Url) {
        ...
        return new ParameterHandler.RelativeUrl(method, p);
    } else if (annotation instanceof Path) {
        ...
        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());
    } else if (annotation instanceof Query) {
        ...
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
            return new ParameterHandler.Query<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations);
            return new ParameterHandler.Query<>(name, converter, encoded).array();
        } else {
            Converter<?, String> converter = retrofit.stringConverter(type, annotations);
            return new ParameterHandler.Query<>(name, converter, encoded);
        }
    } else if (annotation instanceof QueryName) {
        ...
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
            return new ParameterHandler.QueryName<>(converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations);
            return new ParameterHandler.QueryName<>(converter, encoded).array();
        } else {
            Converter<?, String> converter = retrofit.stringConverter(type, annotations);
            return new ParameterHandler.QueryName<>(converter, encoded);
        }
    } else if (annotation instanceof QueryMap) {
        ...
        Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);
        return new ParameterHandler.QueryMap<>(method, p,
                valueConverter, ((QueryMap) annotation).encoded());
    } else if (annotation instanceof Header) {
        ...
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
            return new ParameterHandler.Header<>(name, converter).iterable();
        } else if (rawParameterType.isArray()) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations);
            return new ParameterHandler.Header<>(name, converter).array();
        } else {
            Converter<?, String> converter = retrofit.stringConverter(type, annotations);
            return new ParameterHandler.Header<>(name, converter);
        }
    } else if (annotation instanceof HeaderMap) {
        ...
        Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);
        return new ParameterHandler.HeaderMap<>(method, p, valueConverter);
    } else if (annotation instanceof Field) {
        ...
        if (Iterable.class.isAssignableFrom(rawParameterType)) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(iterableType, annotations);
            return new ParameterHandler.Field<>(name, converter, encoded).iterable();
        } else if (rawParameterType.isArray()) {
            ...
            Converter<?, String> converter = retrofit.stringConverter(arrayComponentType, annotations);
            return new ParameterHandler.Field<>(name, converter, encoded).array();
        } else {
            Converter<?, String> converter = retrofit.stringConverter(type, annotations);
            return new ParameterHandler.Field<>(name, converter, encoded);
        }
    } else if (annotation instanceof FieldMap) {
        ...
        Converter<?, String> valueConverter = retrofit.stringConverter(valueType, annotations);
        return new ParameterHandler.FieldMap<>(method, p, valueConverter, ((FieldMap) annotation).encoded());
    } else if (annotation instanceof Part) {
        ...
        String partName = part.value();
        if (partName.isEmpty()) {
            if (Iterable.class.isAssignableFrom(rawParameterType)) {
                ...
                return ParameterHandler.RawPart.INSTANCE.iterable();
            } else if (rawParameterType.isArray()) {
                ...
                return ParameterHandler.RawPart.INSTANCE.array();
            } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
                return ParameterHandler.RawPart.INSTANCE;
            } else {
                ...
            }
        } else {
            Headers headers =
                    Headers.of("Content-Disposition", "form-data; name=\"" + partName + "\"",
                            "Content-Transfer-Encoding", part.encoding());

            if (Iterable.class.isAssignableFrom(rawParameterType)) {
                ...
                Converter<?, RequestBody> converter =
                        retrofit.requestBodyConverter(iterableType, annotations, methodAnnotations);
                return new ParameterHandler.Part<>(method, p, headers, converter).iterable();
            } else if (rawParameterType.isArray()) {
                ...
                Converter<?, RequestBody> converter =
                        retrofit.requestBodyConverter(arrayComponentType, annotations, methodAnnotations);
                return new ParameterHandler.Part<>(method, p, headers, converter).array();
            } else if (MultipartBody.Part.class.isAssignableFrom(rawParameterType)) {
                ...
            } else {
                Converter<?, RequestBody> converter =
                        retrofit.requestBodyConverter(type, annotations, methodAnnotations);
                return new ParameterHandler.Part<>(method, p, headers, converter);
            }
        }
    } else if (annotation instanceof PartMap) {
        ...
        Converter<?, RequestBody> valueConverter =
                retrofit.requestBodyConverter(valueType, annotations, methodAnnotations);
        return new ParameterHandler.PartMap<>(method, p, valueConverter, partMap.encoding());
    } else if (annotation instanceof Body) {
        ...
        Converter<?, RequestBody> converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        return new ParameterHandler.Body<>(method, p, converter);
    } else if (annotation instanceof Tag) {
        ...
        return new ParameterHandler.Tag<>(tagType);
    }
    return null; // Not a Retrofit annotation.
}

由於方法註解很多,parseParameterAnnotation()代碼非常的多,因此這裏我只把核心的內容展示給大家。parseParameterAnnotation()解析每個參數的過程大致可以總結爲三步

  • 校驗參數是否設置正確,錯誤則拋出異常告知用戶
  • 生成特定的參數數據轉換器
  • 返回對應的ParameterHandler實例

校驗過程就不細說了,感興趣的同學可以自行查閱源碼,我們重點關注參數數據轉換器是如何生成的。從上面的源碼可以看出生成轉換器的方式主要有兩種,一種是通過Retrofit.stringConverter()方法生成,這類轉換器轉換出來的數據類型爲String;另一種則通過Retrofit.requestBodyConverter()方法生成,對應的轉換結果就是請求實體類型RequestBody

先來看Retrofit.stringConverter()

// Retrofit
public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    for (int i = 0, count = converterFactories.size(); i < count; i++) {
        Converter<?, String> converter =
                converterFactories.get(i).stringConverter(type, annotations, this);
        if (converter != null) {
            //noinspection unchecked
            return (Converter<T, String>) converter;
        }
    }

    // Nothing matched. Resort to default converter which just calls toString().
    //noinspection unchecked
    return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
}

這裏的代碼主要完成了以下工作:按順序遍歷數據轉換器工廠集合converterFactories,到符合要求的轉換器工廠,即在實現接口方法Converter.Factory.stringConverter()時不能返回null。這裏有一點細節需要注意,在遍歷過程中只要發現符合的轉換器工廠就會返回,因此轉換器工廠添加的順序決定了它們的優先級越早添加的優先級越高,就可以優先被徵調。還記得我們在構建Retrofit對象時添加的轉換器嗎?現在我們重新來回顧一下加深記憶

// Retrofit.Builder.build()
converterFactories.add(new BuiltInConverters());// 添加Retrofit內置的數據轉換器,優先級最高
converterFactories.addAll(this.converterFactories);// 按順序添加所有自定義的數據轉換器
converterFactories.addAll(platform.defaultConverterFactories());// 添加針對對應運行平臺設置的數據轉換器工廠

當內置轉換器工廠BuiltInConverters以及集合中其他轉換器工廠都不符合要求時,則返回特定的轉換器BuiltInConverters.ToStringConverter,其代碼如下

// BuiltInConverters
static final class ToStringConverter implements Converter<Object, String> {
    static final ToStringConverter INSTANCE = new ToStringConverter();

    @Override
    public String convert(Object value) {
        return value.toString();
    }
}

代碼很簡單就不細說了,接下來是Retrofit.requestBodyConverter()

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) {
    checkNotNull(type, "type == null");
    checkNotNull(parameterAnnotations, "parameterAnnotations == null");
    checkNotNull(methodAnnotations, "methodAnnotations == null");

    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;
        }
    }

    // 沒有找到符合要求的請求數據轉換器,拋出IllegalArgumentException異常
    ...
}

和之前的處理邏輯差別不大,不過這次調用的方法爲Converter.Factory.requestBodyConverter(),而且我們發現BuiltInConverters實現了該接口方法,代碼如下

// BuiltInConverters
@Override
public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
                                               Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
        return RequestBodyConverter.INSTANCE;
    }
    return null;
}

這裏返回了針對RequestBody類型參數的轉換器BuiltInConverters.RequestBodyConverter

// BuiltInConverters
static final class RequestBodyConverter implements Converter<RequestBody, RequestBody> {
    static final RequestBodyConverter INSTANCE = new RequestBodyConverter();

    @Override
    public RequestBody convert(RequestBody value) {
        return value;
    }
}

可以看見RequestBodyConverter並沒有對參數數據進行任何處理,因此該轉換器最主要的作用是攔截類型爲RequestBody的參數

至此我們就完了解析方法參數的全部工作,也成功生成了請求體對象工廠RequestFactory

3.4 生成CallAdapter

HttpServiceMethod是ServiceMethod的子類,它的代碼有點多,我們直接挑重點看。首先是parseAnnotations()方法

// HttpServiceMethod
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
        Retrofit retrofit, Method method, RequestFactory requestFactory) {
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();

    ...

    Converter<ResponseBody, ResponseT> responseConverter =
            createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}

這裏主要做了下面這些工作

  • 通過createCallAdapter()方法生成請求適配器callAdapter
  • 通過callAdapter.responseType()方法獲取響應數據的類型responseType
  • 通過createResponseConverter()方法生成響應體(Response)的數據轉換器responseConverter
  • 通過上述實例構建HttpServiceMethod對象

首先我們分析生成請求適配器CallAdapter的過程,以下是createCallAdapter()方法的代碼

// HttpServiceMethod
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
        Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();
    Annotation[] annotations = method.getAnnotations();
    try {
        //noinspection unchecked
        return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
}
// Retrofit
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
                                         Annotation[] annotations) {
    ...
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
        CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
            return adapter;
        }
    }

    // 當所有適配器都不符合要求時拋出IllegalArgumentException異常
    ...
}

可以發現套路基本上是一樣的,具體方式爲遍歷callAdapterFactories找到符合要求的請求適配器後返回,驗證的方法爲CallAdapter.Factory.get()。通過上一章的分析我們知道,除了用戶添加的自定義的請求適配器工廠以外,Retrofit還內置了兩種默認的適配器工廠:CompletableFutureCallAdapterFactory(系統版本大於等於24時生效)和ExecutorCallAdapterFactory。這裏我們只以ExecutorCallAdapterFactory爲例進行分析,來看下源碼

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
    final Executor callbackExecutor;

    ExecutorCallAdapterFactory(Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
    }

    @Override
    public @Nullable
    CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(Call<Object> call) {
                return new ExecutorCallbackCall<>(callbackExecutor, call);
            }
        };
    }
    ...
}

通過上述代碼我們知道,get()方法指定ExecutorCallAdapterFactory的適配目標是返回類型爲retrofit2.Call的API方法,其最終返回值是一個新的CallAdapter實例。此外,該CallAdapter實例的adapt()方法生成了retrofit2.Call的子類ExecutorCallbackCall,這實際上是請求執行器的裝飾器,具體內容我們在後面用到時再細說,現在繼續下一步

3.5 生成ResponseConverter

接下來是調用HttpServiceMethod.createResponseConverter()方法生成響應數據轉換器(ResponseConverter),我們來看下相關代碼

// HttpServiceMethod
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
        Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    try {
        return retrofit.responseBodyConverter(responseType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(method, e, "Unable to create converter for %s", responseType);
    }
}
// Retrofit
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
}

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
        @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
        Converter<ResponseBody, ?> converter =
                converterFactories.get(i).responseBodyConverter(type, annotations, this);
        if (converter != null) {
            //noinspection unchecked
            return (Converter<ResponseBody, T>) converter;
        }
    }

    // 無法找到符合要求的ResponseBodyConverter時拋出IllegalArgumentException異常
    ...
}

同樣這裏也是通過遍歷轉換器工廠集合converterFactories找到符合要求的轉換器工廠,驗證方法爲Converter.Factory.responseBodyConverter()。之前講過轉換器工廠優先級最高的是內置的BuiltInConverters,來看下它的responseBodyConverter()方法

final class BuiltInConverters extends Converter.Factory {
    ...

    @Override
    public @Nullable 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;
    }
    ...
}

這裏指定BuiltInConverters只能處理類型爲okhttp3.ResponseBody以及void的響應體數據。當類型爲ResponseBody時,還會判斷接口方法是否含有@Streaming註解,然後提供不同的轉換器實例。下面我們就看下這三個數據轉換器有什麼區別吧

// BuiltInConverters
static final class StreamingResponseBodyConverter implements Converter<ResponseBody, ResponseBody> {
    static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) {
        return value;
    }
}

static final class BufferingResponseBodyConverter implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) throws IOException {
        try {
            // Buffer the entire body to avoid future I/O.
            return Utils.buffer(value);
        } finally {
            value.close();
        }
    }
}

static final class VoidResponseBodyConverter implements Converter<ResponseBody, Void> {
    static final VoidResponseBodyConverter INSTANCE = new VoidResponseBodyConverter();

    @Override
    public Void convert(ResponseBody value) {
        value.close();
        return null;
    }
}

可以看到這三個轉換器都實現了Converter接口的convert()方法,這個方法會在後續處理響應數據時調用,現在我們先看下這三個轉換器的convert()方法是如何實現的

  • StreamingResponseBodyConverter:會將數據流的連接返回給用戶,那爲什麼返回的是數據流的連接而不是數據呢?原因是RessponseBody持有的數據可能會很大,OkHttp並不會將數據直接保存到內存中,實際保存的是數據流的連接,當用戶需要時再通過連接從服務端獲取數據。因此@Streaming註解適合在執行大文件下載任務的時候使用
  • BufferingResponseBodyConverter:先將響應體數據保存至內存緩衝區中,再返回給用戶。這適合在響應體數據較小的場景下使用,也是默認的數據處理方式
  • VoidResponseBodyConverter:當設置返回的響應體數據類型爲void時,說明用戶無意處理響應的數據,那麼直接關閉釋放資源即可

現在回到一開始的請求示例中,我們定義的接口方法是這樣的

public interface ExpressService {
    @GET("query?type=ems&postid=11111111")
    Call<ResponseBody> get();
}

這裏設置的響應數據類型是ResponseBody,即BuiltInConverters可以處理這個API方法,因此遍歷查找轉換器工廠的過程到了BuiltInConverters處就被攔截了下來,且由於該接口方法並沒有添加@Streaming註解,所以最終HttpServiceMethod.createResponseConverter()構建的響應數據轉換器就是BufferingResponseBodyConverter

3.6 完成API方法的動態代理

完成所有前置工作後,我們回到Retrofit.create()方法,由之前的分析可知API方法會被動態代理到

loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

loadServiceMethod()最終返回的是HttpServiceMethod的實例,因此我們來看下HttpServiceMethod.invoke()方法的代碼

// HttpServiceMethod
@Override
ReturnT invoke(Object[] args) {
    return callAdapter.adapt(new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}

adapt()方法的傳參是OkHttpCall的實例,這是Retrofit層的請求執行器,其內部持有真實的請求執行器okhttp3.Call。OkHttpCall作爲代理類主要負責Retrofit層與OkHttp層之間交互數據的轉換

此外,從之前的講解中我們知道adapt()方法的返回結果是ExecutorCallbackCall的實例,因此動態代理API方法最終得到的就是這個實例,其作爲OkHttpCall的裝飾器,主要負責協同回調執行器爲OkHttpCall動態增強回調方面的功能

至此API方法的動態代理和加載配置的過程就分析完了,下一章我們將分析Retrofit是如何發起請求以及處理響應數據的

3.7 本章小結

這一章分析了加載API方法配置的過程,原理是利用動態代理機制,主要完成了以下工作:

  • 通過解析方法註解參數註解生成了【請求對象工廠】以及【參數數據轉換器
  • 通過解析方法返回類型響應數據類型生成了【請求適配器工廠】、【響應數據轉換器工廠】、【請求執行器(裝飾器)

4. 請求與響應

本章我們將分析請求與響應的過程,以下是對應的示例代碼

call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {

    }
});

4.1 處理回調

由上一章我們知道,這裏的call實際上是裝飾器ExecutorCallbackCall,我們來看下ExecutorCallbackCall.enqueue()方法的代碼

// ExecutorCallAdapterFactory
static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
        this.callbackExecutor = callbackExecutor;
        this.delegate = delegate;
    }

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

        delegate.enqueue(new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, final Response<T> response) {
                callbackExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        if (delegate.isCanceled()) {
                            // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                            callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                        } else {
                            callback.onResponse(ExecutorCallbackCall.this, response);
                        }
                    }
                });
            }

            @Override
            public void onFailure(Call<T> call, final Throwable t) {
                callbackExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callback.onFailure(ExecutorCallbackCall.this, t);
                    }
                });
            }
        });
    }

    ...
}

ExecutorCallbackCall有兩個成員變量,callbackExecutordelegate,通過之前的分析我們知道它們分別對應了MainThreadExecutorOkHttpCall。那麼先來分析MainThreadExecutor.execute()方法,該方法在OkHttpCall的請求回調中被調用

// Platform.Android
static class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

    @Override
    public void execute(Runnable r) {
        handler.post(r);
    }
}

這裏的功能非常好理解,綜合之前的代碼來看就是將OkHttpCall的請求回調結果發送至主線程。再來看OkHttpCall.enqueue()方法

// OkHttpCall
@Override
public void enqueue(final Callback<T> callback) {
    okhttp3.Call call;
    call = rawCall = createRawCall();
    ...
    call.enqueue(new okhttp3.Callback() {
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response = parseResponse(rawResponse);
            callback.onResponse(OkHttpCall.this, response);
            ...
        }

        @Override
        public void onFailure(okhttp3.Call call, IOException e) {
            callback.onFailure(OkHttpCall.this, e);
        }
        ...
    });
}

這裏我把核心的代碼摘抄出來進行分析,首先是通過createRawCall()方法獲取真實的請求執行器

4.2 生成okhttp3.Call

// OkHttpCall
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

這裏有兩個需要關注的點,一是調用callFactory.newCall()方法獲取真實的請求執行器,callFactory實際上就是OkHttpClient對象的引用,這和我們在OkHttp中生成請求執行器的方法一致;二是通過requestFactory.create()方法獲取請求體okhttp3.Request的實例,其中args是通過動態代理API方法拿到的傳參。現在我們開始分析生成Request對象的過程

// RequestFactory
okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    ...
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
            headers, contentType, hasBody, isFormEncoded, isMultipart);

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
        argumentList.add(args[p]);
        handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.get()
            .tag(Invocation.class, new Invocation(method, argumentList))
            .build();
}

handlers是參數封裝類ParameterHandler的集合,通過apply()方法可以處理參數數據,轉換成RequestBuilder需要的格式,然後通過RequestBuilder的引用設置數據,這些數據將在生成Request時用到。先來看下RequestBuilder的構造方法

// RequestBuilder
RequestBuilder(String method, HttpUrl baseUrl,
               @Nullable String relativeUrl, @Nullable Headers headers, @Nullable MediaType contentType,
               boolean hasBody, boolean isFormEncoded, boolean isMultipart) {
    this.method = method;
    this.baseUrl = baseUrl;
    this.relativeUrl = relativeUrl;
    this.requestBuilder = new Request.Builder();
    this.contentType = contentType;
    this.hasBody = hasBody;

    if (headers != null) {
        requestBuilder.headers(headers);
    }

    if (isFormEncoded) {
        // Will be set to 'body' in 'build'.
        formBuilder = new FormBody.Builder();
    } else if (isMultipart) {
        // Will be set to 'body' in 'build'.
        multipartBuilder = new MultipartBody.Builder();
        multipartBuilder.setType(MultipartBody.FORM);
    }
}

可以看到我們之前在解析註解、加載API方法配置的過程中生成的屬性在這都得到了應用。再來看ParameterHandler.apply()的過程,我們以請求示例中定義的API方法爲例,方法參數中使用了@Query註解,對應的參數封裝類是Query

// ParameterHandler
static final class Query<T> extends ParameterHandler<T> {
    private final String name;
    private final Converter<T, String> valueConverter;
    private final boolean encoded;

    Query(String name, Converter<T, String> valueConverter, boolean encoded) {
        this.name = checkNotNull(name, "name == null");
        this.valueConverter = valueConverter;
        this.encoded = encoded;
    }

    @Override
    void apply(RequestBuilder builder, @Nullable T value) throws IOException {
        if (value == null) return; // Skip null values.

        String queryValue = valueConverter.convert(value);
        if (queryValue == null) return; // Skip converted but null values

        builder.addQueryParam(name, queryValue, encoded);
    }
}

參數數據轉換器在這裏開始發揮作用了,可以通過調用Converter.convert()方法將參數數據轉換成RequestBuilder實際需要的數據,通過3.3.2一節的分析我們知道處理String類型數據的轉換器是ToStringConverter,因此這裏valueConverter就是ToStringConverter實例的引用,重新回顧一下ToStringConverter.convert()方法

// BuiltInConverters.ToStringConverter
@Override
public String convert(Object value) {
    return value.toString();
}

數據轉換完畢後,繼續調用RequestBuilder.addQueryParam()方法設置Query參數的數據

// RequestBuilder
void addQueryParam(String name, @Nullable String value, boolean encoded) {
    if (relativeUrl != null) {
        // Do a one-time combination of the built relative URL and the base URL.
        urlBuilder = baseUrl.newBuilder(relativeUrl);
        ...
        relativeUrl = null;
    }

    if (encoded) {
        //noinspection ConstantConditions Checked to be non-null by above 'if' block.
        urlBuilder.addEncodedQueryParameter(name, value);
    } else {
        //noinspection ConstantConditions Checked to be non-null by above 'if' block.
        urlBuilder.addQueryParameter(name, value);
    }
}

到這裏就都是OkHttp構建Request的方法了,不再展開細說。回到RequestFactory.create(),繼續往下看Request的構建過程

// RequestFactory.create
return requestBuilder.get()
        .tag(Invocation.class, new Invocation(method, argumentList))
        .build();

首先調用了RequestBuilder.get()方法,看下源碼

Request.Builder get() {
    HttpUrl url;
    HttpUrl.Builder urlBuilder = this.urlBuilder;
    if (urlBuilder != null) {
        url = urlBuilder.build();
    } else {
        // No query parameters triggered builder creation, just combine the relative URL and base URL.
        //noinspection ConstantConditions Non-null if urlBuilder is null.
        url = baseUrl.resolve(relativeUrl);
        ...
    }

    RequestBody body = this.body;
    if (body == null) {
        // Try to pull from one of the builders.
        if (formBuilder != null) {
            body = formBuilder.build();
        } else if (multipartBuilder != null) {
            body = multipartBuilder.build();
        } else if (hasBody) {
            // Body is absent, make an empty body.
            body = RequestBody.create(null, new byte[0]);
        }
    }

    MediaType contentType = this.contentType;
    if (contentType != null) {
        if (body != null) {
            body = new ContentTypeOverridingRequestBody(body, contentType);
        } else {
            requestBuilder.addHeader("Content-Type", contentType.toString());
        }
    }

    return requestBuilder
            .url(url)
            .method(method, body);
}

同樣這裏也都是OkHttp層的調用,成員變量requestBuilder對應的類型就是okhttp3.Request.Builder,調用build()方法就可以生成Request對象了

至此OkHttpCall.createRawCall()成功創建了一個okhttp3.Call的實例,現在我們回到OkHttpCall.enqueue()方法繼續往下看

4.3 處理okhttp3.Response

得到okhttp3.Call實例後,下一步就是調用Call.enqueue()發起異步請求,並在回調中得到響應體對象okhttp3.Response,由於這是OkHttp層的,因此還需要解析轉換成Retrofit層的響應體對象,解析轉換的方法是OkHttpCall.parseResponse()

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

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();

    int code = rawResponse.code();
    if (code < 200 || code >= 300) {
        try {
            // Buffer the entire body to avoid future I/O.
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            return Response.error(bufferedBody, rawResponse);
        } finally {
            rawBody.close();
        }
    }

    if (code == 204 || code == 205) {
        rawBody.close();
        return Response.success(null, rawResponse);
    }

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
        T body = responseConverter.convert(catchingBody);
        return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
        // If the underlying source threw an exception, propagate that rather than indicating it was
        // a runtime exception.
        catchingBody.throwIfCaught();
        throw e;
    }
}

這裏的重點是調用ResponseConverter.convert()轉換響應數據,之前分析過的內置和自定義的響應數據轉換器在這發揮了作用,ResponseBody成功轉換成用戶需要的類型

至此我們完成了okhttp3.Response的處理過程,下一步再將生成的retrofit2.Response對象通過回調方法返回給用戶,請求與響應的過程以及整個Retrofit工作流程的源碼分析就完成了

4.4 本章小結

這一章分析了請求與響應的過程,主要完成了以下工作:

  • 通過上一章生成的請求執行器代理類發起異步請求
  • 通過RequestFactory構建請求體Request,其中包括使用參數數據轉換器處理請求參數
  • 通過CallFactory生成真實請求執行器okhttp3.Call,並調用enqueue()方法發起真實的異步請求
  • 處理OkHttp層響應對象okhttp3.Response,生成Retrofit層響應對象retrofit2.Response,其中包括使用響應數據轉換器將響應數據轉換成用戶定義的形式
  • 請求執行器代理類中使用回調執行器將響應結果返還給用戶

總結

最後以一張Retrofit的工作流程圖總結之前的分析(ps:爲了和流程進行區分,這裏我將一些重要成員抽象成了數據源)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章