一、什么是Retrofit
A type-safe HTTP client for Android and Java
以上是官網給出的答案,翻譯成中文:Retrofit是Android和Java上類型安全的http客戶端。
簡而言之,Retrofit是對http網絡請求框架的封裝,與Volley\AsyncHttpClient等類似,簡化了用戶的http請求流程。
它支持DELETE\GET\POST\PUT\HEADER\OPTION\PATCH等請求方式,默認使用okhttp作為網絡請求引擎。
二、Retrofit的一些重要類及接口
- Call
該接口代表一個實際的http請求,它的主要作用有三個:
- 調用createRawCall創建請求;
- 調用okhttp發送請求;
- 將okhttp返回的響應解析成Retrofit對應的Response
- 對于異步的enqueue操作,還有執行相應的onResponse和onFailure回調操作
拋開mock測試相關,該類的實現類主要有:OkHttpCall\ExecutorCallbackCall。
- CallAdapter
顧名思義,該接口的主要作用是適配,將一個帶有響應類型為R的Call請求適配成需要的類型T。所以該接口的定義是CallAdapter<R,T>。其核心方法T adapt(Call<R> call)的作用就在于此。
該類內部還封裝了一個抽象工廠類Factory。通過Retrofit.create會產生一個接口對象,該接口對象內部的方法的返回值都具有返回類型。Factory正是用于創建基于該返回類型而生的CallAdapter。
拋開mock測試相關,CallAdapter的實現類主要有:BodyCallAdapter\ResponseCallAdapter\RxJavaCallAdapter\RxJava2CallAdapter。
拋開mock測試相關,CallAdapter.Factory的實現類分布在retrofit-adapters包中,有GuavaCallAdapterFactory\Java8CallAdapterFactory\RxJavaCallAdapterFactory\RxJava2CallAdapterFactory\ScalaCallAdapterFactory。
- Converter
Converter主要有三個方法,對應三個作用。
- 當請求帶有請求體時,將請求體轉換成OkHttp對應的RequestBody
- 將響應解析成需要的實體對象T
- 將Field\FieldMap\Header\HeaderMap\Path\Query\QueryMap等的值轉換成String類型
當需要自定義數據轉換機制時,可以實現該接口,并注冊到Retrofit中。目前默認的數據轉換采用Gson,也可根據需要自定義成xml\protoBuf\fastJson等。
三、根據設計模式分析源碼
首先放一張stay大神畫的Retrofit請求的流程圖
接下來我們將根據這張流程圖的走勢,結合遇到的設計模式,分析Retrofit的設計源碼。對于一些常見的、簡單的設計模式,本文不做分析。
-
外觀(門面)模式
我們知道,在使用Retrofit庫時,基本是圍繞著Retrofit這個類本身來使用的。該類內部封裝了不同的組件,如CallAdapter\ServiceMethod\Converter等。
我們在使用Retrofit時,并沒有直接與CallAdapter\ServiceMethod\Converter等打交道,而是通過Retrofit提供的接口間接去使用這些組件。這種對外提供簡單統一的接口、隱蔽內部子系統的具體實現、將變化隔離的設計方式稱為“外觀模式”,也叫“門面模式”。
//不同組件被封裝在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> adapterFactories; final @Nullable Executor callbackExecutor; final boolean validateEagerly;
-
建造者模式
在使用Retrofit前,需要先創建出Retrofit對象。Retrofit的構造方法的訪問權限是protected級別的,非繼承類無法直接通過new Retrofit的方式創建Retrofit對象,同時Retrofit類又是final類型的,因此無法被繼承。也就是說,直接通過new Retrofit來構造Retrofit對象的方式是行不通的。
幸運的是,Retrofit內部提供了Builder建造者類,用于構建Retrofit對象,也就出現了我們使用Retrofit時的通用寫法:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
通過Builder的build方法,最終生成一個Retrofit對象。這里需要詳細分析一下構建流程,以便我們理解Builder中幾個重要對象的默認賦值,也便于更好地理解后續文章的分析。
//無參的構造方法默認調用帶有platform參數的構造方法 public Builder() { this(Platform.get()); } Builder(Platform platform) { this.platform = platform; }
在創建Builder對象時,最終會調用帶有platForm參數的構造方法。Platform又是什么呢?顧名思義,它代表的是平臺的意思,我們跟蹤進PlatForm源碼查看,會發現內部封裝了一個靜態私有變量PLATFORM。毫無疑問,這就是我們在Builder構造時傳入的參數了。該參數通過findPlatform靜態方法獲取。
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(); }
當ClassLoader可以查找到android.os.Build,則代表該平臺是Android;
當ClassLoader可以查找到java.util.Optional,則代表是Java8;
否則直接返回Platform。Android和Java8都繼承自Platform。
//Android類定義 static class Android extends Platform { ... } //Java8類定義 static class Java8 extends Platform { ... }
顯然,我們的程序是運行在Android上的,因此該處優先返回的自然是Android對象。Android的內部結構如下,大家先熟悉一下,關注重點是ExecutorCallAdapterFactory和MainThreadExecutor。這兩個結構在接下來將起到重要作用。
先小小地劇透一下MainThreadExecutor的作用:它是用來將異步請求的回調從子線程切換到主線程執行的!!!
static class Android extends Platform { @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); return new ExecutorCallAdapterFactory(callbackExecutor); } static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } }
創建完Builder對象之后,接下來就是調用Builder的build方法來構建出具體的Retrofit對象了。
public Retrofit build() { okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); List<Converter.Factory> converterFactories = new ArrayList<>(1 + this.converterFactories.size()); converterFactories.add(new BuiltInConverters()); converterFactories.addAll(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
build方法里面主要執行了以下幾個初始化操作:
1. 判斷callFactory是否為null,,顯然,由于使用了無參的Builder構造方法,因此callFactory==null,callFactory默認被賦值為OkHttpClient,即默認使用okhttp作為網絡請求引擎。
2. 判斷callbackExecutor是否為null,同樣callbackExecutor==null,進入到callbackExecutor = platform.defaultCallbackExecutor(),根據上述分析,這里調用的是Android的defaultCallbackExecutor(),最終返回MainThreadExecutor對象。
3. adapterFactories添加適配器工廠,默認添加了platform.defaultCallAdapterFactory(callbackExecutor),回溯到Android類中,發現該方法返回ExecutorCallAdapterFactory對象。
4. converterFactories添加轉換器工廠,默認內置了BuiltInConverters,同時也添加用戶自定義的轉換器。
執行完以上初始化操作后,最終會調用Retrofit的構造方法創建對象。
-
動態代理
在創建完Retrofit對象后,還記得接下來的使用姿勢嗎?
是的,我們還需要一個接口,在這個接口里面需要預定義一些方法,這些方法使用Retrofit提供的注解聲明參數,范式如下:
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }
聲明完接口之后,接下來就是調用Retrofit的create方法返回一個接口代理對象,而后就可以通過這個代理對象調用接口中的方法了,范式如下:
GitHubService service = retrofit.create(GitHubService.class); Call<List<Repo>> repos = service.listRepos("octocat");
create方法是Retrofit動態代理的核心所在。
public <T> T create(final Class<T> service) { //傳入的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(); @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // 如果方法隸屬于Object,則當做普通方法處理 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //如果是接口里default方法(Java8新特性),則調 //用默認方法處理 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
動態代理與裝飾者模式很像,都是通過攔截某些方法,在這些方法前后做一些附加操作。但動態代理又與裝飾者模式又存在區別,個人認為,主要有以下不同:
1. 動態代理更傾向于用在當有較多的方法需要被攔截處理的情況,即批量處理的情況;
2. 裝飾者模式傾向于用在需要攔截的方法的量較少的情況,即單個操作的情況;create方法里,通過Proxy的newProxyInstance構造出一個代理對象,Retrofit給該代理對象傳遞了一個InvocationHandler,之后我們所有經過代理對象調用的接口方法,都會被該InvocationHandler攔截處理。
在InvocationHandler的invoke方法里,會讀取接口里聲明的方法參數,并生成對應的Call,之后將該Call適配成程序需要的類型返回。
首先,invoke方法里需要先獲取ServiceMethod對象。ServiceMethod的類名很直觀地表明了它就是我們在GithubService接口里定義的方法。
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
可以看到,loadServiceMethod首先會到serviceMethodCache緩存中去取,如果可以取到ServiceMethod對象,就直接返回;否則調用ServiceMethod的Builder建造器去構造,并將構造結果緩存到serviceMethodCache中。
接下來我們一起來看看ServiceMethod.Builder的build方法,瞧一瞧它在里面到底做了哪些事。
public ServiceMethod build() { //創建callAdapter和convert對象 callAdapter = createCallAdapter(); responseType = callAdapter.responseType(); responseConverter = createResponseConverter(); //提取方法級別的注解 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //提取參數級別的注解,生成不同的參數處理器,分發處理 int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } return new ServiceMethod<>(this); }
以上我們省略掉了部分邏輯,只留下核心部分,便于分析。在build方法里,第一步先調用createCallAdapter和createResponseConverter創建callAdapter和responseConverter對象。
之后遍歷接口方法的所有注解,調用parseMethodAnnotation處理每個方法級別的注解。方法級別的注解是用于方法上的,例如@GET@POST@Headers@Multipart@FormUrlEncoded等。該方法定義如下。
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); if (!Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); } } 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("@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } }
這部分邏輯一目了然,不做贅述。
提取完方法級別的注解,接下來就是提取參數級別的注解了。參數級別的注解就是在方法形參上添加的注解標記。
private ParameterHandler<?> parseParameter( int p, Type parameterType, Annotation[] annotations) { ParameterHandler<?> result = null; for (Annotation annotation : annotations) { ParameterHandler<?> annotationAction = parseParameterAnnotation( p, parameterType, annotations, annotation); if (annotationAction == null) { continue; } result = annotationAction; } return result; }
從以上代碼分析可知,parseParameter遍歷每個注解,并調用了parseParameterAnnotation去解析注解,之后返回參數處理器。parseParameterAnnotation的代碼太多,但邏輯很清晰,這里僅貼出部分示例代碼,具體詳細代碼請讀者自行翻閱源碼閱讀。
if (annotation instanceof Url) { gotUrl = true; if (type == HttpUrl.class || type == String.class || type == URI.class || (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) { return new ParameterHandler.RelativeUrl(); } } else if (annotation instanceof Path) { gotPath = true; Path path = (Path) annotation; String name = path.value(); validatePathName(p, name); Converter<?, String> converter = retrofit.stringConverter(type, annotations); return new ParameterHandler.Path<>(name, converter, path.encoded()); }else if(...){ ... }
-
策略模式
對于每個參數級別的注解,最終都會返回ParameterHandler.Xxx對象。ParameterHandler.Xxx是ParameterHandler的靜態內部類,這些內部類都是繼承自ParameterHandler,對應具體的參數注解類型。ParameterHandler.Xxx的繼承采用了策略模式,每個ParameterHandler的子類都實現了各自的apply方法。這部分代碼也請讀者自行翻閱,不難理解。
策略模式與模板方法模式非常類似,導致很多讀者可能對它們的概念有些混淆。這里簡單說一下個人對這兩種設計模式的區別的理解。
這兩種模式都是在父類定義一個抽象的或者空的算法接口,而具體的算法實現則延遲到子類實現。區別就在于,模板方法模式更多地是傾向于算法流程中的某些節點算法的延遲實現,也就是從算法流程中拆分出的一部分子算法;策略模式注重的是單一算法,其本身就是一個完整的算法實現,無需拆分。
經過上述漫長的解析過程,構成ServiceMethod所需的成員的賦值終于準備完成,最終,ServiceMethod.Builder.build方法返回了new ServiceMethod對象。
至此,我們已經分析到了Retrofit.create在動態代理時解析注解封裝進ServiceMethod的過程。相信大家也需要一點時間消化,我們先休息會,稍后回來~~~~