Retrofit原始碼解析
一,前言
Retrofit其實是將OKhttp封裝起來,和volley一樣。那解析Retrofit其實就是解析它如何封裝使用OKhttp,那我直接從其使用上來跟蹤原始碼。
總體上可以分為四步:
- 建立Retrofit物件
- 通過Retrofit獲取介面物件
- 通過介面物件獲取請求服務的Call物件
- 最後通過Call的非同步enqueue或同步execute來執行網路請求並回調結果。
下面也是通過這四步來解析原始碼
二,建立Retrofit物件
先佔時不考慮RxJava的使用
Retrofit retrofit = new Retrofit.Builder()//1 .baseUrl(baseUrl)//2 .addConverterFactory(GsonConverterFactory.create()) //3 .build();//4
Retrofit是通過建造者模式 來構建物件的,那很明顯,這裡只是配置了請求網路的引數而已,而這些配置在後面的網路請求和結果轉換用到了。
第二步是為了配置請求服務的地址;第三部是配置返回結果的轉換器,使結果為物件。
2.1 Builder()
public static final class Builder { private final Platform platform; // 網路請求器的工廠 private @Nullable okhttp3.Call.Factory callFactory; // 網路請求的url地址 private HttpUrl baseUrl; // 資料轉換器工廠的集合,生產資料轉換器(converter) private final List<Converter.Factory> converterFactories = new ArrayList<>(); // 網路請求介面卡工廠的集合,生產網路請求介面卡(CallAdapter) private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>(); //// 請求結束後回撥方法的執行Executor private @Nullable Executor callbackExecutor; // 是否提前對業務介面中的註解進行驗證轉換的標誌位 private boolean validateEagerly; Builder(Platform platform) { this.platform = platform; // 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()); } public Builder() { this(Platform.get()); } ... }
Builder裡儲存的引數都會在Retrofit建立時賦值給它,後面的build()再介紹。
在建立Builder()物件時,其實只賦值了兩個屬性:platform 和converterFactories。可以知道預設的請求結果轉換器為BuiltInConverters,我們開發中一般使用GsonConverterFactory.create()。
2.1.1 Platform
Platform是單例模式 ,通過Platform.get()來獲取物件。
class Platform { //單例物件是通過findPlatform()建立的 private static final Platform PLATFORM = findPlatform(); static Platform get() { return PLATFORM; } private static Platform findPlatform() { try { //如果是安卓系統,就返回Android() Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android(); } } catch (ClassNotFoundException ignored) { } try { // Java8() Class.forName("java.util.Optional"); return new Java8(); } catch (ClassNotFoundException ignored) { } return new Platform(); } static class Android extends Platform { //請求結果回撥使用的Executor,如果Builder沒有賦值給callbackExecutor,就會呼叫這個方法賦值 @Override public Executor defaultCallbackExecutor() { return new MainThreadExecutor(); } //Builder裡的CallAdapter.Factory,最後會在build()裡賦值給adapterFactories,作為預設的網路請求介面卡 @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); //那可以看出預設的網路介面卡是ExecutorCallAdapterFactory實現的 return new ExecutorCallAdapterFactory(callbackExecutor); } //呼叫handler使回撥在主執行緒執行 static class MainThreadExecutor implements Executor { private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable r) { handler.post(r); } } } }
最為安卓平臺,Platform最終賦值的物件是靜態內部類Android,這個物件作為預設配置,會在Builder的build()方法中呼叫賦值。
defaultCallbackExecutor是返回回撥執行的Executor,返回的MainThreadExecutor是使用handler 讓回撥操作在主執行緒執行。如果在Builder中沒有指定回撥的Executor,會呼叫defaultCallbackExecutor指定非同步網路請求的回撥在主執行緒執行 。
defaultCallAdapterFactory則是指定預設網路介面卡工廠為ExecutorCallAdapterFactory ,呼叫介面方法返回的Call ,就是這個類建立的。具體等後面使用其功能再介紹。
2.2 其它配置
2.2.1 baseUrl
就是將String型別的url,經過合格性檢測,拆分儲存,再轉換成HttpUrl賦值給baseUrl。
public Builder baseUrl(String baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); //將String的轉為HttpUrl HttpUrl httpUrl = HttpUrl.parse(baseUrl); if (httpUrl == null) { throw new IllegalArgumentException("Illegal URL: " + baseUrl); } return baseUrl(httpUrl); } public Builder baseUrl(HttpUrl baseUrl) { checkNotNull(baseUrl, "baseUrl == null"); List<String> pathSegments = baseUrl.pathSegments(); //如果路徑結尾不是以"/"結尾,會丟擲異常 if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl); } this.baseUrl = baseUrl; return this; }
值得注意的是,路徑必須以"/"結尾
2.2.2 addConverterFactory
//將轉換工廠儲存到converterFactories中,在構造器中,已經add了一個BuiltInConverters public Builder addConverterFactory(Converter.Factory factory) { converterFactories.add(checkNotNull(factory, "factory == null")); return this; }
上面的例子是新增GsonConverterFactory,下面來看看它是如何轉換的。
2.2.2.1 GsonConverterFactory
public final class GsonConverterFactory extends Converter.Factory { @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); //返回對應的Converter物件 return new GsonResponseBodyConverter<>(gson, adapter); } } final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> { @Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { //將ResponseBody轉為泛型物件 return adapter.read(jsonReader); } finally { value.close(); } }
通過convert轉換的型別,是在呼叫responseBodyConverter建立物件時傳入的請求介面的返回類的泛型。也就是例子中的HttpResult。
如果要自定義Converter 來實現請求結果轉化,按上面那樣就可以了,使用工廠方法模式 。
- 繼承Converter,在convert方法中返回轉換後的結果,比如GsonRequestBodyConverter中是把ResponseBody轉成自己需要轉的型別。
- 繼承Converter.Factory,在responseBodyConverter中將相應的Converter物件返回。
- 在Retrofit的創造器中呼叫addConverterFactory新增相應的Converter.Factory。
2.3 build()
這部將根據先前的配置生成Retrofit,其實Builder中的配置屬性幾乎都賦值給了Retrofit。
public Retrofit build() { //callFactory是類okhttp3.Call.Factory的物件,如果自己沒有設定了OkHttp物件,則會自己建立一個。 okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } //回撥方法執行器 Executor callbackExecutor = this.callbackExecutor; //沒有指定的話,就用構造器中賦值的platform中的,也就是將回調放在主執行緒 if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // 將配置的網路介面卡進行保護性拷貝 List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); //新增預設的網路介面卡,也就是ExecutorCallAdapterFactory adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // 與網路介面卡的排序不同,預設的BuiltInConverters放在第一位,後面才是新增的 List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
對於網路介面卡佇列儲存順序和資料轉化器的佇列順序不同 ,其實是在後面給ServiceMethod賦值相應屬性時,採取了不同的取值策略。網路介面卡優先獲取佇列前面的;資料轉化器雖然也是從佇列前面開始適配,但預設的BuiltInConverters要介面返回型別為ResponseBody或Void才會返回Converter物件,返回型別為其它時,會繼續遍歷後面的資料轉化器,所以在設定返回值為物件時,GsonConverterFactory會起作用。具體後面ServiceMethod建立時會介紹。
2.4 總結
在最後build()中,可以發現建立Retrofit時,傳入了6個引數。
- OKHttp物件。比如設定連線超時時間的OKHttp
- 伺服器地址。必填
- 請求結果的資料轉換器。對於請求結果的返回型別進行轉換
- 網路介面卡。預設為ExecutorCallAdapterFactory,通常設定為RxJava2CallAdapterFactory.create()。
- 回撥方法執行器。預設回撥在主執行緒執行。
- 是否提前對業務介面中的註解進行驗證轉換的標誌位。在create中有用到。
比較重要的類有設定預設回撥方法執行器和預設網路介面卡的Platform;預設的資料轉換器BuiltInConverters。
三 通過Retrofit獲取介面物件
MovieService movieService = retrofit.create(MovieService.class); public interface MovieService { //get請求 @GET("top250") //網路請求資料的方法,@Query配置請求引數 Call<HttpResult<List<Subject>>> getTopMovie(@Query("start")int start, @Query("count")int count); }
MovieService是個介面,裡面定義了一個方法,通過註解上面配置了請求介面的引數配置,retrofit通過方法的引數建立介面物件。
那來看看如何通過介面來獲取物件。
3.1 create
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); //是否提前通過註解加驗證載入請求介面,會將介面中的方法都解析成ServiceMethod,並加入serviceMethodCache快取中 if (validateEagerly) { eagerlyValidateMethods(service); } //建立動態代理物件,用來代理介面 return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() {//將代理類的實現交給 InvocationHandler類作為具體的實現 private final Platform platform = Platform.get(); //被代理執行的方法,是通過在InvocationHandler中的invoke方法呼叫的 @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // Object的方法直接呼叫原方法 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //android預設為false,不走 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //這裡才是被代理方法真正執行的地方 //通過介面方法的註解來獲取ServiceMethod ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); //如果是預設網路介面卡,就生成Call物件;RxJava則生成Observable物件 return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
代理模式的動態代理是實現介面方法呼叫的關鍵,動態代理也只能代理介面,invoke方法是介面方法正在實現的地方,生成的代理物件在執行時存在記憶體中。
這樣
那接下來看介面方法的呼叫。
四 通過介面物件獲取請求服務的Call物件
Call<HttpResult<List<Subject>>> call = movieService.getTopMovie(0, 10);
這裡的getTopMovie方法呼叫,是在上面invoke方法中實現的,主要是這幾行程式碼
//通過方法註解配置,獲取ServiceMethod ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); //根據ServiceMethod建立OkHttpCall OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); //建立Call return serviceMethod.callAdapter.adapt(okHttpCall);
4.1 loadServiceMethod
ServiceMethod<?, ?> loadServiceMethod(Method method) { //從快取中獲取,如果先前建立過久直接用 //create中的eagerlyValidateMethods方法會提前建立物件並快取 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; }
物件的建立是通過new ServiceMethod.Builder<>(this, method).build()實現的。
4.1.1 new ServiceMethod.Builder<>(this, method)
Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; //獲取方法裡的註解 this.methodAnnotations = method.getAnnotations(); //獲取方法的引數型別 this.parameterTypes = method.getGenericParameterTypes(); //獲取方法引數裡的註解 this.parameterAnnotationsArray = method.getParameterAnnotations(); }
就是賦值。
4.1.2 build()
public ServiceMethod build() { //1.根據介面的返回型別和註解,從retrofit中獲取網路介面卡 callAdapter = createCallAdapter(); //方法返回值泛型的型別,也就是網路請求的返回值型別 responseType = callAdapter.responseType(); //2.根據方法的返回型別和註解,網路資料轉換器 responseConverter = createResponseConverter(); //3.獲取方法上的註解 for (Annotation annotation : methodAnnotations) { //根據註解獲取配置 parseMethodAnnotation(annotation); } //4.獲取方法引數裡的註解 int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } //根據註解獲取配置引數 parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } return new ServiceMethod<>(this); }
重點關注下網路介面卡和資料轉換器是如何獲得的,根據前面的Builder可知,retrofit裡它們都是以陣列儲存的,那獲取規則又如何呢。
4.1.2.1 createCallAdapter
先看網路介面卡如何獲取的。
private CallAdapter<T, R> createCallAdapter() { //返回值型別 Type returnType = method.getGenericReturnType(); //方法註解 Annotation[] annotations = method.getAnnotations(); try { //通過retrofit的callAdapter方法返回的 return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations); } catch (RuntimeException e) { // Wide exception range because factories are user code. throw methodError(e, "Unable to create call adapter for %s", returnType); } } public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { //從0開始 int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { //在分析的例子中,沒有配置網路介面卡,所以用的預設的ExecutorCallAdapterFactory //使用RxJava會配置了RxJava2CallAdapterFactory CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } }
因為例子沒有配置網路介面卡
這裡是使用預設的網路介面卡ExecutorCallAdapterFactory,如果配置了RxJava2CallAdapterFactory,它會在預設網路介面卡的前面,那時就會使用RxJava2CallAdapterFactory了。ExecutorCallAdapterFactory這個到後面通過網路介面卡返回的Call呼叫enqueue時再介紹。
4.1.2.2 createResponseConverter
解析的例子中,是配置了GsonConverterFactory資料轉換器。和上面的網路介面卡一樣,資料轉換器也是從retrofit儲存的配置資訊中獲取的
public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { //從0開始 int start = converterFactories.indexOf(skipPast) + 1; for (int i = start, count = converterFactories.size(); i < count; i++) { //預設的資料轉換器BuiltInConverters,如果介面方法返回值型別不是ResponseBody和Void的話,返回null //分析的這個例子中,介面方法返回值的泛型是HttpResult,所以會迴圈到下個GsonConverterFactor物件 Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } } }
資料轉換器把預設的BuiltInConverters放在了第一位,但還是根據介面方法返回值泛型會過濾選擇。所以在自定義轉換器時,要過濾要轉換的型別 。
4.2 ExecutorCallAdapterFactory
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); serviceMethod.callAdapter.adapt(okHttpCall)
又回到Retrofit中create的動態代理中來,到invoke中最後一步,返回Call。(如果是RxJava,則返回Observable)
根據上面的分析,可以知道serviceMethod.callAdapter物件是預設的ExecutorCallAdapterFactory。callAdapter和Converter都採用了工廠方法模式 來實現,理解這個更有助於自定義它們。
final class ExecutorCallAdapterFactory extends CallAdapter.Factory { //返回方法執行器 final Executor callbackExecutor; ExecutorCallAdapterFactory(Executor callbackExecutor) { this.callbackExecutor = callbackExecutor; } @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { //返回值型別判斷,要是Call才行 if (getRawType(returnType) != Call.class) { return null; } //返回值泛型的型別,如例子中的HttpResult final Type responseType = Utils.getCallResponseType(returnType); //返回CallAdapter return new CallAdapter<Object, Call<?>>() { @Override public Type responseType() { return responseType; } @Override public Call<Object> adapt(Call<Object> call) { //相應的call物件 return new ExecutorCallbackCall<>(callbackExecutor, call); } }; } }
CallAdapter和它的工廠CallAdapter.Factory,是自定義CallAdapter 的關鍵。
- CallAdapter的adapt方法返回介面方法的返回值,上面是返回Call物件。如果是RxJava,則返回Observable物件。
- CallAdapter.Factory則是建立CallAdapter的工廠,它的get方法返回CallAdapter物件
- CallAdapter的adapt方法的返回物件,是實現後續網路請求的關鍵,自定義要在返回物件中將自己封裝的網路請求放在裡面。
4.3 總結
到這裡,如何通過介面物件呼叫方法獲取Call已經很明瞭了。就是通過動態代理物件裡的InvocationHandler裡的invoke方法實現,呼叫介面物件方法實際上是呼叫invoke方法。
在invoke中,首先根據介面方法的註解和Retrofit裡的配置通過建造者模式生成ServiceMethod物件,再生成實現網路請求的物件OkHttpCall,通過ServiceMethod中的CallAdapter來生成封裝了OkHttpCall的Call物件(RxJava則是Observable)。
自定義CallAdapter的關鍵是實現CallAdapter和CallAdapter.Factory。
五 請求網路
請求網路時通過呼叫call.enqueue,那來看看預設的Call物件相應方法的實現,也就是ExecutorCallAdapterFactory裡的ExecutorCallbackCall
static final class ExecutorCallbackCall<T> implements Call<T> { //回撥方法執行器 final Executor callbackExecutor; //OkHttpCall物件,是生成ExecutorCallbackCall時傳入的 final Call<T> delegate; //在CallAdapter的adapt方法中被建立賦值 ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } @Override public void enqueue(final Callback<T> callback) { checkNotNull(callback, "callback == null"); //代理模式,最終還是OkHttpCall實現 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()) { //失敗回撥 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用的代理模式,最終實現的物件的OkHttpCall。
OkHttp的使用就分四步
- 建立okHttpClient物件
- 建立一個Request
- new call
- 請求加入排程
真正實現網路請求的就是OKHttp,我們可以從上面四步瞭解網路請求的實現。第一步在建立retrofit物件時就已經完成了。
5.1 OkHttpCall
OkHttpCall是retrofit2中封裝的一個類,內部呼叫還是通過ServiceMethod獲取網路請求配置並生成okhttp3.Call來實現。
@Override public void enqueue(final Callback<T> callback) { 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 { //1.生成okhttp3.Call物件,預設是OkHttpClient的newCall建立,為RealCall物件 call = rawCall = createRawCall(); } catch (Throwable t) { failure = creationFailure = t; } } } //2.傳送非同步網路請求 call.enqueue(new okhttp3.Callback() { @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException { Response<T> response; try { //3.通過存在serviceMethod的Converter轉換資料型別 response = parseResponse(rawResponse); } catch (Throwable e) { callFailure(e); return; } //4.回撥介面 callSuccess(response); } @Override public void onFailure(okhttp3.Call call, IOException e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } private void callFailure(Throwable e) { try { callback.onFailure(OkHttpCall.this, e); } catch (Throwable t) { t.printStackTrace(); } } private void callSuccess(Response<T> response) { try { callback.onResponse(OkHttpCall.this, response); } catch (Throwable t) { t.printStackTrace(); } } }); }
可以看出實現網路請求的是OKhttp,那隻要關注okhttp3.Call是如何建立和網路請求結果如何轉換就好了。
5.1.1 createRawCall()
okhttp3.Call的建立就完成了OkHttp使用的前三步了,下面來看看前三部是怎麼實現的吧。
所有的網路配置資訊都在ServiceMethod中,很明顯,它們的生成也都在ServiceMethod中,通過預設的OkHttpClient的newCall建立,為RealCall物件,這些還是後面看OKHttp的解析瞭解吧。
private okhttp3.Call createRawCall() throws IOException { //建立Request物件,將請求網路的資訊都封裝在裡面 Request request = serviceMethod.toRequest(args); //callFactory是okhttp3.Call.Factory,在建立Retrofit時預設的OkHttpClient //正常OKhttp生成Call的流程 okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
建立Request,再建立okhttp3.Call,這是正常的OkHttp請求的第二三步。這裡只關心Retrofit的封裝,就看toRequest方法了。
5.1.1.1 serviceMethod.toRequest
Request toRequest(@Nullable Object... args) throws IOException { //創造者模式構建物件 RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart); //介面方法的引數的註解結合 ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers; //校驗介面方法的引數的註解數量和介面方法引數的數量是否一致 int argumentCount = args != null ? args.length : 0; if (argumentCount != handlers.length) { throw new IllegalArgumentException("Argument count (" + argumentCount + ") doesn't match expected count (" + handlers.length + ")"); } //對介面方法引數,會在apply中轉換引數型別,並新增到requestBuilder中。 //資料轉換預設使用BuiltInConverters.ToStringConverter.INSTANCE,也就是直接將引數值toString() for (int p = 0; p < argumentCount; p++) { handlers[p].apply(requestBuilder, args[p]); } //裡面通過引數,用普通的OKhttp的建造器生成Request return requestBuilder.build(); } //例子中,新增的請求引數是Query,所以上面handlers[p].apply的實現是 static final class Query<T> extends ParameterHandler<T> { @Override void apply(RequestBuilder builder, @Nullable T value) throws IOException { if (value == null) return; // Skip null values. //這裡就是把value.toString()了 String queryValue = valueConverter.convert(value); if (queryValue == null) return; // Skip converted but null values //新增到請求url裡去 builder.addQueryParam(name, queryValue, encoded); } }
對於介面方法註解的解析,我就不做太多介紹了。handlers[p].apply是將方法引數的配置資訊新增到requestBuilder中去,將引數配置都新增後,在build中,用OKhttp的Request.Builder生成Request物件。
RequestBuilder將baseUrl和請求介面方法裡的註解配置資訊組合起來,將配置資訊新增入Request.Builder中,建立Request。
5.2 parseResponse
call.enqueue這個方法是okhttp3.Call的方法,就不分析了,主要分析請求服務結束後,將資料型別轉換為回撥介面需要的型別。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); // 移除Response中的響應正文,在此基礎上建立一個新的Response 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); } //將響應正文封裝一層 ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody); try { //呼叫資料轉換器進行型別轉換 T body = serviceMethod.toResponse(catchingBody); //將轉換後內容和空body的Response再組合成Retrofit裡的Response 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; } }
這裡將okhttp3.Response,將響應正文通過資料轉換器轉換,再和空響應正文的okhttp3.Response組合成Retrofit裡的Response,用於回撥使用。
callback.onResponse是回調了預設網路介面卡裡的ExecutorCallbackCall裡的Callback,然後ExecutorCallbackCall裡再使用預設的Executor在主執行緒回撥例子裡的Callback。
六 總結
- Converter和CallAdapter都是用工廠方法模式實現的,自定義時要分別實現產品類和工廠類,在工廠類中建立產品。
- Retrofit是通過建造者模式建立的,Retrofit.Builder()主要配置:網路請求工廠(okhttp3.Call.Factory),網路請求地址(HttpUrl),資料轉換器(List<Converter.Factory>),網路介面卡(List<CallAdapter.Factory>),回撥執行方法(Executor),是否提前對業務介面中的註解進行驗證轉換的標誌位(validateEagerly)
- 介面方法裡通過註解配置了網路請求的配置,通過Retrofit的create方法,動態代理 實現了介面方法。介面方法的返回值是網路介面卡 返回物件,型別要和介面方法的返回值一致。
- 動態代理的invoke方法裡,通過Retrofit的配置和介面方法的註解配置生成ServiceMethod 物件,這個物件儲存了網路請求的所有配置。大部分方法和解析註解生成配置有關 ,主要關注兩個方法:一個toReques t通過RequestBuilder生成OKhttp的Request,toResponse 可以利用資料轉換器將ResponseBody轉為請求介面方法返回值泛型。這兩個方法都是給OkHttpCall使用。
- 再通過ServiceMethod建立了OkHttpCall ,裡面有請求網路的方法,實際上是根據ServiceMethod的配置網路資訊生成的Request再生成okhttp3.Call,將請求結果通過ServiceMethod的配置轉換器進行資料轉換,最終回撥給呼叫它的類(預設ExecutorCallbackCall)。
- ExecutorCallAdapterFactory 是預設的網路介面卡工廠,在動態代理的invoke方法裡,建立ServiceMethod時用get方法生成介面卡後,再通過介面卡的adapt生成ExecutorCallbackCall 返回值,ExecutorCallbackCall中用靜態代理模式實現,被代理物件是OkHttpCall。
- GsonConverterFactory 的具體轉換結果型別,是在建立ServiceMethod時,用解析的介面方法中的返回值泛型確定的。
- 隨後再列下關鍵的幾個類:
- Retrofit:用建立者模式儲存網路配置。
- ServiceMethod:通過介面方法註解的配置和Retrofit裡的配置,生成ServiceMethod。toReques和toResponse方法供給OkHttpCall使用。
- OkHttpCall:網路請求的封裝類,內部結合ServiceMethod的toReques生成OKHttp實現網路請求,再用ServiceMethod的toResponse轉換結果回撥給呼叫它的物件,這個物件是網路介面卡adapt生成。