【Android】Retrofit原始碼分析
Retrofit簡介
retrofit
n. 式樣翻新,花樣翻新 vt. 給機器裝置裝配(新部件),翻新,改型
Retrofit 是一個 RESTful 的 HTTP 網路請求框架的封裝。注意這裡並沒有說它是網路請求框架,主要原因在於網路請求的工作並不是 Retrofit
來完成的。Retrofit
2.0 開始內建 OkHttp
,前者專注於介面的封裝,後者專注於真正的網路請求。即通過 大量的設計模式 封裝了 OkHttp
,使得簡潔易用。
我們的應用程式通過 Retrofit
請求網路,實際上是使用 Retrofit
介面層封裝請求引數、Header、Url 等資訊,之後由 OkHttp
OkHttp
將原始的結果交給 Retrofit
,後者根據使用者的需求對結果進行解析的過程。Retrofit的大概原理過程如下:
Retrofit
將Http
請求 抽象 成Java
介面- 在接口裡用 註解 描述和配置 網路請求引數
- 用動態代理 的方式,動態將網路請求介面的註解 解析 成
HTTP
請求 - 最後執行
HTTP
請求
這篇文章我將從Retrofit的基本用法出發,按照其使用步驟,一步步的探究Retrofit的實現原理及其原始碼的設計模式。
Retrofit Github: https://github.com/square/retrofit
使用步驟
使用 Retrofit
非常簡單,首先需要在 build.gradle 中新增依賴:
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
如果需要使用Gson解析器,也需要在build.gradle中新增依賴(後文會詳細講到Retrofit的Converter):
implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
1. 定義Interface
Retrofit使用java interface和註解描述了HTTP請求的API引數,比如Github的一個API:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
其中Repo類定義如下:
public class Repo {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2. 建立Retrofit例項
這樣呼叫Retrofit就會為上面這個interface自動生成一個實現類:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create()) //新增Gson解析器
.build();
GitHubService service = retrofit.create(GitHubService.class);
3. 發起請求
然後呼叫interface的具體方法時(這裡是listRepos()
)就構造好了一個Call
,
Call<List<Repo>> call = service.listRepos("octocat");
返回的 call
其實並不是真正的資料結果,只是封裝成了一個隨時可以執行的請求,需要在合適的時機去執行它:
// 同步呼叫
List<Repo> data = repos.execute();
// 非同步呼叫
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
List<Repo> data = response.body();
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
t.printStackTrace();
}
});
怎麼樣,有沒有突然覺得請求介面就好像訪問自家的方法一樣簡單?下面我們轉入原始碼分析,按照使用步驟來探索Retrofit的實現原理。
原始碼分析
我們先來看看Retrofit的例項化步驟。先看看Retrofit這個類的引數
public final class Retrofit {
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory; // 生產網路請求器(Call)的工廠,預設使用OkHttp
final HttpUrl baseUrl; // url地址
final List<Converter.Factory> converterFactories; // 資料轉換器(converter)工廠
final List<CallAdapter.Factory> callAdapterFactories; // 生產網路請求介面卡(CallAdapter)的工廠List
final @Nullable Executor callbackExecutor; // 回撥方法執行器
final boolean validateEagerly; // 是否提前對業務介面中的註解進行驗證轉換的標誌位
//建構函式
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
//...省略其他程式碼...
}
Retrofit的建構函式是package包可見的,並不是public的,所以外部並不能直接new,而是要通過Retrofit.Builder
來例項化。
Retrofit.Builder
Retrofit.Builder
是 Retrofit 類中的一個子類,負責用來建立 Retrofit 例項物件,使用『Builder模式』的好處是清晰明瞭可定製化。
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
Builder(Platform platform) {
this.platform = platform;
}
//建構函式
public Builder() {
// Retrofit支援Android Java8 預設Platform 三種平臺,此處確認當前是在哪個平臺環境執行
this(Platform.get());
}
//...省略其他程式碼...
}
可以看到Retrofit.Builder
中的成員變數跟Retrofit中基本上是一一對應的,就不過多解釋了。這裡可以看到Builder的建構函式中預設判斷了一下當前的執行平臺。
最後,在建立 Retrofit.Builder
物件並進行自定義配置後,我們就要呼叫 build()
方法來構造出 Retrofit
物件了。那麼,我們來看下 build()
方法裡幹了什麼:
public Retrofit build() {
// 必須要配置baseUrl
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 預設為 OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// Android 平臺下預設為 MainThreadExecutor
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);
// 新增預設的 ExecutorCallAdapterFactory
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
// Add the built-in converter factory first. This prevents overriding its behavior but also
// ensures correct behavior when using converters that consume all types.
// 首先新增預設的 BuiltInConverters
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
在 build()
中,做的事情有:檢查配置、設定預設配置、建立 Retrofit
物件。並且在執行 .build()
方法前,只有 .baseUrl()
是必須呼叫來設定訪問地址的,其餘方法則是可選的。同時我們可以看到設定了很多預設成員,但這裡我們重點關注四個成員:callFactory
,callAdapter
,responseConverter
和 parameterHandlers
。
callFactory
負責建立 HTTP 請求,HTTP 請求被抽象為了okhttp3.Call
類,它表示一個已經準備好,可以隨時執行的 HTTP 請求;callAdapter
負責把retrofit2.Call<?>
裡的Call
轉換為 另一種型別T
(注意和okhttp3.Call
區分開來,retrofit2.Call<?>
表示的是對一個 Retrofit 方法的呼叫),這個過程會發送一個 HTTP 請求,拿到伺服器返回的資料(通過okhttp3.Call
實現),並把資料轉換為宣告的T
型別物件(通過Converter<F, T>
實現);responseConverter
是Converter<ResponseBody, T>
型別,負責把伺服器返回的資料(JSON、XML、PB、二進位制或者其他格式,由ResponseBody
封裝)轉化為T
型別的物件;parameterHandlers
則負責解析 API 定義時每個方法的引數,並在構造 HTTP 請求時設定引數;
CallAdapter和Converter到底是幹什麼的?
這裡多插兩句,給大家解釋一下這個CallAdapter
和Converter
到底是幹什麼的?我們知道,最簡單的Retrofit介面一般定義如下:
public interface GitHubService {
@GET("users/{user}/repos")
Call<ResponseBody> listRepos(@Path("user") String user);
}
在給Retrofit不新增任何CallAdapterFactory
的情況下,介面方法的返回型別必須是Call<?>
,不能是其他型別。因而Retrofit提供了對這個Call進行轉換為其他型別的功能,那就是CallAdapter
。比如新增一個RxJava的CallAdapter:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // RxJava轉換器
.build();
然後我們就可以這樣定義介面:
interface MyService {
@GET("/user")
Observable<User> getUser();
}
如果添加了一個Java8的CallAdapter,就可以這樣定義介面:
interface MyService {
@GET("/user")
CompletableFuture<User> getUser();
}
Retrofit提供的CallAdapter:
CallAdapter | Gradle依賴 |
---|---|
guava | com.squareup.retrofit2:adapter-guava:latest.version |
Java8 | com.squareup.retrofit2:adapter-java8:latest.version |
rxjava | com.squareup.retrofit2:adapter-rxjava:latest.version |
rxjava2 | com.squareup.retrofit2:adapter-rxjava2:latest.version |
scala | com.squareup.retrofit2:adapter-scala:latest.version |
同樣地,如果在不給Retrofit新增任何ConverterFactory
的情況下,介面方法返回型別Call<T>
裡的泛型T
必須是ResponseBody
,而不能是其他型別(比如List<User>
),這就是Converter
的作用,直白點也就是資料解析器,負責把ResponseBody
解析成List<User>
。
另外,在我們構造 HTTP 請求時,我們傳遞的引數都是使用的註解型別(諸如 Path
,Query
,Field
等),那 Retrofit 是如何把我們傳遞的各種引數都轉化為 String 的呢?還是由 Retrofit
類提供Converter
!
Converter.Factory
除了提供responseBodyConverter,還提供 requestBodyConverter 和 stringConverter,API 方法中除了 @Body
和 @Part
型別的引數,都利用 stringConverter 進行轉換,而 @Body
和 @Part
型別的引數則利用 requestBodyConverter 進行轉換。
Retrofit提供的Converter
Converter | Gradle依賴 |
---|---|
Gson | com.squareup.retrofit2:converter-gson:latest.version |
Guava | com.squareup.retrofit2:converter-guava:latest.version |
Jackson | com.squareup.retrofit2:converter-jackson:latest.version |
Java8 | com.squareup.retrofit2:converter-java8:latest.version |
Jaxb | com.squareup.retrofit2:converter-jaxb:latest.version |
Moshi | com.squareup.retrofit2:converter-moshi:latest.version |
Protobuf | com.squareup.retrofit2:converter-protobuf:latest.version |
Scalars | com.squareup.retrofit2:converter-scalars:latest.version |
Wire | com.squareup.retrofit2:converter-wire:latest.version |
Simple XML | com.squareup.retrofit2:converter-simplexml:latest.version |
Platform
這裡我們再來個小插曲,來看下 Retrofit 是如何確定當前執行的是哪個平臺環境的。
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的callbackExecutor
@Nullable
Executor defaultCallbackExecutor() {
return null;
}
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE;
}
boolean isDefaultMethod(Method method) {
return false;
}
@Nullable
Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object,
@Nullable Object... args) throws Throwable {
throw new UnsupportedOperationException();
}
}
從上面的程式碼中可以看到,是通過反射判斷有沒有該類來實現的。比較巧妙,大家可以的學習一下。
而此處的Android
和Java8
均是Platform的子類:
static class Android extends Platform {
//Android平臺的預設callbackExecutor,實際上就是拋到UI執行緒去執行回撥
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
//Android平臺的預設CallAdapterFactory
@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);
}
}
}
Java8的定義如下:
@IgnoreJRERequirement // Only classloaded and used on Java 8.
static class Java8 extends Platform {
//判斷被呼叫的method是否Java8的預設方法
@Override
boolean isDefaultMethod(Method method) {
return method.isDefault();
}
//呼叫預設方法
@Override
Object invokeDefaultMethod(Method method, Class<?> declaringClass, Object object,
@Nullable Object... args) throws Throwable {
// Because the service interface might not be public, we need to use a MethodHandle lookup
// that ignores the visibility of the declaringClass.
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true);
return constructor.newInstance(declaringClass, -1 /* trusted */)
.unreflectSpecial(method, declaringClass)
.bindTo(object)
.invokeWithArguments(args);
}
}
Java 8 的interface引入了新的語言特性——預設方法(Default Methods)。大家可以參考這篇文章:Java8 預設方法 default method
預設方法允許您新增新的功能到現有庫的介面中,並能確保與採用舊版本介面編寫的程式碼的二進位制相容性。
所以如果在Java8的平臺上使用Retrofit的話,Retrofit需要排除我們定義的interface中的這些Default Methods。
在使用Retrofit.Builder
例項化得到 Retrofit
物件後就是呼叫 Retrofit#create()
方法來建立我們 API 介面的例項。
Retrofit
所以我們需要跟進Retrofit類中的 create(final Class<T> service)
方法來看下:
public <T> T create(final Class<T> service) {
// 校驗是否為介面,且不能繼承其他介面
Utils.validateServiceInterface(service);
// 是否需要提前解析介面方法
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 動態代理模式, 返回一個 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, 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);
}
// 將介面中的方法構造為 ServiceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
在上面的程式碼中,最關鍵的就是『動態代理』,返回了一個 Proxy 代理類,呼叫介面中的任何方法都會呼叫 proxy 裡的 invoke 方法。實際上,進行網路操作的都是通過代理類來完成的。簡單的說,在我們前面的示例程式碼中呼叫 GitHubService.listRepos
時,實際上呼叫的是這裡的 InvocationHandler.invoke
方法。
InvocationHandler.invoke
這個方法的意思是:如果呼叫的是 Object
的方法,例如 equals
,toString
,那就直接呼叫。如果是 default 方法(Java 8 引入的新語法),就呼叫 default 方法。這些我們都先不管,因為我們在Android平臺呼叫 listRepos
,肯定不是這兩種情況,那這次呼叫真正幹活的就是這三行程式碼了:
// 將介面中的方法構造為 ServiceMethod
ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.adapt(okHttpCall);
這三句程式碼,下面我們著重來看。
在代理中,會根據引數中傳入的具體介面方法來構造出對應的 serviceMethod
。ServiceMethod
類的作用就是把介面的方法適配為對應的 HTTP call 。
ServiceMethod<?, ?> loadServiceMethod(Method method) {
// 先從快取中取,若沒有就去建立對應的 ServiceMethod
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;
}
可以看到在內部還維護了一個 serviceMethodCache
來快取 ServiceMethod
,同一個 API 的同一個方法,只會建立一次。由於我們每次獲取 API 例項都是傳入的 class
物件(比如示例中的GitHubService.class),而 class
物件是程序內單例的,所以獲取到它的同一個方法 Method
例項也是單例的,所以這裡的快取是有效的。我們就直接來看 ServiceMethod
是如何被建立的吧。
ServiceMethod
ServiceMethod<R, T>
類的作用正如其 JavaDoc 所言:
Adapts an invocation of an interface method into an HTTP call. 把對介面方法的呼叫轉為一次 HTTP 呼叫。
一個 ServiceMethod
物件對應於一個 API interface 的一個方法,上面的loadServiceMethod(method)
方法負責載入了 ServiceMethod
。我們發現 ServiceMethod
也是通過建造者模式(ServiceMethod.Builder
)來建立物件的。那就進入對應構造方法:
// 位於ServiceMethod類中
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 介面方法的註解
this.methodAnnotations = method.getAnnotations();
// 介面方法的引數型別
this.parameterTypes = method.getGenericParameterTypes();
// 介面方法引數的註解
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
在構造方法中沒有什麼特別的地方,我們單刀直入 build()
方法:
public ServiceMethod build() {
// 根據介面方法的註解和返回型別建立 callAdapter
// 如果沒有新增 CallAdapter 那麼預設會用 ExecutorCallAdapterFactory
callAdapter = createCallAdapter();
// calladapter 的響應型別中的泛型,比如 Call<User> 中的 User
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// 根據之前泛型中的型別以及介面方法的註解建立 ResponseConverter
responseConverter = createResponseConverter();
// 根據介面方法的註解構造請求方法,比如 @GET @POST @DELETE 等
// 另外還有新增請求頭,檢查url中有無帶?,轉化 path 中的引數
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
// 若無 body 則不能有 Multipart 和 FormEncoded 的註解
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
// 解析介面方法引數中的註解,比如 @Path @Query @QueryMap @Field 等等
// 相應的,每個方法的引數都建立了一個 ParameterHandler<?> 物件
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);
}
// 檢查構造出的請求有沒有不對的地方
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
在 build()
中程式碼挺長的,總結起來就一句話:就是將 API 介面中的方法進行解析,構造成 ServiceMethod
,交給下面的 OkHttpCall
使用。
基本上做的事情就是:
- 建立 CallAdapter ;
- 建立 ResponseConverter;
- 根據 API 介面方法的註解構造網路請求方法;
- 根據 API 介面方法引數中的註解構造網路請求的引數;
- 檢查有無異常;
下面我們看看第二句重要的程式碼OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
OkHttpCall
OkHttpCall
實現了 retrofit2.Call
,我們通常會使用它的 execute()
和 enqueue(Callback<T> callback)
介面。前者用於同步執行 HTTP 請求,後者用於非同步執行。我們先看 execute()
@Override
public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
// 根據 serviceMethod 中的眾多資料創建出 Okhttp 中的 Request 物件
// 注意的一點,會呼叫上面的 ParameterHandler.apply 方法來填充網路請求引數
// 然後再根據 OkhttpClient 創建出 Okhttp 中的 Call
// 這一步也說明了在 Retrofit 中的 OkHttpCall 內部請求最後會轉換為 OkHttp 的 Call
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
// 執行 call 並轉換成響應的 response
return parseResponse(call.execute());
}
在 execute()
做的就是將 Retrofit 中的 call 轉化為 OkHttp 中的 Call 。最後讓 OkHttp 的 Call 去執行。
主要包括三步:
- 呼叫
createRawCall()
建立了okhttp3.Call
,包括構造引數; - 使用
call.execute()
執行網路請求; - 解析網路請求返回的資料;
我們分別來看看createRawCall()
和parseResponse()
這兩個方法:
// OkHttpCall類中
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = serviceMethod.toCall(args);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
createRawCall()
函式中,我們呼叫了 serviceMethod.toCall(args)
來建立 okhttp3.Call
,而在後者中,我們之前準備好的 parameterHandlers
就派上了用場。
然後我們再呼叫 serviceMethod.callFactory.newCall(request)
來建立 okhttp3.Call
,這裡之前準備好的 callFactory
同樣也派上了用場,由於工廠在構造 Retrofit
物件時可以指定,所以我們也可以指定其他的工廠(例如使用過時的 HttpURLConnection
的工廠),來使用其它的底層 HttpClient 實現。
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();
// 如果返回的響應碼不是成功的話,返回錯誤 Response
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();
}
}
// 如果返回的響應碼是204或者205,返回沒有 body 的成功 Response
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
// 將 body 轉換為對應的泛型,然後返回成功 Response
T body = serviceMethod.toResponse(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;
}
}
我們呼叫 okhttp3.Call#execute()
來執行網路請求,這個方法是阻塞的,執行完畢之後將返回收到的響應資料。收到響應資料之後,我們進行了狀態碼的檢查,通過檢查之後我們呼叫了 serviceMethod.toResponse(catchingBody)
來把響應資料轉化為了我們需要的資料型別物件T。
// ServiceMethod類中
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
在serviceMethod
的toResponse
函式中,我們之前準備好的 responseConverter
也派上了用場。我們分別看下:
responseConverter
這個例項哪裡來?ResponseConverter
的convert()
方法幹了什麼?
預設的Converter
#1. responseConverter
這個例項哪裡來?
在ServiceMethod.Builder
類中,我們找到了它的賦值的地方:
// 在ServiceMethod.Builder類中
public ServiceMethod build() {
// 建立預設的CallAdapter
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError("'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
// 建立預設的ResponseConverter
responseConverter = createResponseConverter();
//...省略其他程式碼...
}
很明顯,在build()
方法中同時建立了預設的CallAdapter和ResponseConverter。我們先繼續前往createResponseConverter
關注ResponseConverter:
private Converter<ResponseBody, T> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create converter for %s", responseType);
}
}
原來是根據我們定義的介面方法的返回型別和註解,交給了Retrofit的responseBodyConverter(Type type, Annotation[] annotations)
去找:
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");
// 根據介面方法的返回型別、註解等資訊找到對應的ResponseConverter
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;
}
}
// 找不到任何ResponseConverter的話,就拋異常
StringBuilder builder = new StringBuilder("Could not locate ResponseBody converter for ")
.append(type)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
builder.append('\n');
}
builder.append(" Tried:");
for (int i = start, count = converterFactories.size(); i < count; i++) {
builder.append("\n * ").append(converterFactories.get(i).getClass().getName());
}
throw new IllegalArgumentException(builder.toString());
}
在 Retrofit
類內部,將遍歷一個 converterFactories
列表,讓工廠們提供,如果最終沒有工廠能(根據 returnType
和 annotations
)提供需要的 ResponseConverter
,那將丟擲異常。而這個工廠列表我們可以在構造 Retrofit
物件時進行新增。
還記得我們在使用Retrofit.Builder
構造Retrofit物件的時候,預設新增的converterFactory
嗎?
public Retrofit build() {
//...省略其他程式碼...
// 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()); // 預設的Converter
converterFactories.addAll(this.converterFactories); // 我們自定義的Converter
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
#2. ResponseConverter
的convert()
方法幹了什麼?
我們看看這個內建轉換器(BuildInConverters
)是什麼東西:
final class BuiltInConverters extends Converter.Factory {
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
// 內建的BuiltInConverters直接返回ResponseBody或者Void型別
if (type == ResponseBody.class) {
return Utils.isAnnotationPresent(annotations, Streaming.class)
? StreamingResponseBodyConverter.INSTANCE
: BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
return null;
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (RequestBody.class.isAssignableFrom(Utils.getRawType(type))) {
return RequestBodyConverter.INSTANCE;
}
return null;
}
//...省略其他程式碼...
static final class StreamingResponseBodyConverter
implements Converter<ResponseBody, ResponseBody> {
static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();
@Override
public ResponseBody convert(ResponseBody value) {
// 直接返回,不進行任何處理
return value;
}
}
}
內建的BuiltInConverters
會直接返回ResponseBody
或者Void
型別,不做其他任何的轉換操作,所以如果我們不新增任何ConverterFactory的預設情況下,我們定義的介面方法返回型別只能接受ResponseBody
或者Void
這兩種型別。
預設的CallAdapter
下面,我們來看下那三句重要程式碼中的最後一句return serviceMethod.adapt(okHttpCall);
。ServiceMethod類中的adapt()
方法如下:
// ServiceMethod類中
T adapt(Call<R> call) {
return callAdapter.adapt(call);
}
這裡我們分別看下:
- 這個
callAdapter
例項哪裡來的? CallAdapter
類的adapt()
方法幹了什麼?
#1. 這個callAdapter
例項哪裡來的?
這個callAdapter
例項是在哪裡賦值的呢,我們找到了ServiceMethod.Builder
的build()
方法,可以回到上面看看這個方法的原始碼,可以看到它是呼叫了createCallAdapter
這個方法建立的,如下。
// ServiceMethod.Builder類中
private CallAdapter<T, R> createCallAdapter() {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(
"Method return type must not include a type variable or wildcard: %s", returnType);
}
// 介面方法返回的型別不能是void
if (returnType == void.class) {
throw methodError("Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
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);
}
}
可以看到,callAdapter
還是由 Retrofit
類提供的。
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
// 根據介面方法的返回型別、註解等資訊找到對應的CallAdapter
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;
}
}
// 找不到任何CallAdapter,拋異常
StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
.append(returnType)
.append(".\n");
if (skipPast != null) {
builder.append(" Skipped:");
for (int i = 0; i < start; i++) {
builder.append(