博文出處:Retrofit源碼解析,歡迎大家關注我的博客,謝謝!
Header
之前對 OkHttp 進行過源碼分析了,那么今天就來講講 Retrofit 。
Retrofit 其實是對 OkHttp 進行了一層封裝,讓開發(fā)者對網(wǎng)絡操作更加方便快捷。
相信絕大多數(shù)的 Android 開發(fā)者都有使用過的經歷。其 restful 風格的編程俘獲了眾多人的心。
廢話就不多講了,下面就要對 Retrofit 進行源碼解析。
本文解析的 Retrofit 基于 v2.3.0 ,GitHub 地址:https://github.com/square/retrofit
Retrofit 使用方法
直接抄官網(wǎng)的:
第一步,聲明 API 接口:
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
第二步,構造出 Retrofit 對象:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
第三步,得到 API 接口,直接調用:
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
最后,就是調用 repos 執(zhí)行 Call :
// sync
repos.execute();
// async
repos.enqueue(...);
請求源碼解析
我們先來看看發(fā)出網(wǎng)絡請求部分的源碼。
Retrofit.Builder
首先切入點就是 Retrofit.Builder 。
在 Retrofit.Builder 中有以下的方法:
- client : 設置 http client,默認是 OkHttpClient,會調用
callFactory方法 - callFactory : 設置網(wǎng)絡請求 call 的工廠,默認就是上面的 OkHttpClient
- baseUrl : api 的 base url
- addConverterFactory : 添加數(shù)據(jù)轉換器工廠
- addCallAdapterFactory?。骸√砑泳W(wǎng)絡請求適配器工廠
- callbackExecutor : 回調方法執(zhí)行器
- validateEagerly : 是否提前解析接口方法
這些都是用來配置 Builder 的。
那么我們來看下 Builder 的構造方法:
public Builder() {
// 確定平臺,有 Android Java8 默認Platform 三種
this(Platform.get());
}
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.
// 默認內置的數(shù)據(jù)轉換器 BuiltInConverters
converterFactories.add(new BuiltInConverters());
}
來個小插曲,我們來看下 Retrofit 是如何確定平臺的:
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();
}
從上面的代碼中可以看到,是通過反射判斷有沒有該類來實現(xiàn)的。若以后在開發(fā)的過程中有需要判斷平臺的需求,我們可以直接將該段代碼 copy 過來。
接著,在創(chuàng)建 Builder 對象并進行自定義配置后,我們就要調用 build() 方法來構造出 Retrofit 對象了。那么,我們來看下 build() 方法里干了什么:
public Retrofit build() {
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> adapterFactories = new ArrayList<>(this.adapterFactories);
// 添加 ExecutorCallAdapterFactory
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters. 默認有 BuiltInConverters
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
在 build() 中,做的事情有:檢查配置、設置默認配置、創(chuàng)建 Retrofit 對象。
關于上面種種奇怪的類,我們先不關心,因為之后我們遇到了再分析。我們先把目光聚焦在 Retrofit 類上。
Retrofit
Retrofit 類的構造方法沒什么好看的,在這就不講了。
得到 Retrofit 對象后就是調用 create(final Class<T> service) 方法來創(chuàng)建我們 API 接口的實例。
所以我們需要跟進 create(final Class<T> service) 中來看下:
public <T> T create(final Class<T> service) {
// 校驗是否為接口,且不能繼承其他接口
Utils.validateServiceInterface(service);
// 是否需要提前解析接口方法
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 動態(tài)代理模式
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);
}
});
}
在上面的代碼中,最關鍵的就是動態(tài)代理。實際上,進行網(wǎng)絡操作的都是通過代理類來完成的。如果對動態(tài)代理不太懂的同學請自行百度了,這里就不多講了。
重點就是
// 將接口中的方法構造為 ServiceMethod
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
這三句代碼,下面我們著重來看。
在代理中,會根據(jù)參數(shù)中傳入的具體接口方法來構造出對應的 serviceMethod 。ServiceMethod 類的作用就是把接口的方法適配為對應的 HTTP call 。
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
// 先從緩存中取,若沒有就去創(chuàng)建對應的 ServiceMethod
result = serviceMethodCache.get(method);
if (result == null) {
// 沒有緩存就創(chuàng)建,之后再放入緩存中
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
可以看到在內部還維護了一個 serviceMethodCache 來緩存 ServiceMethod ,以便提高效率。我們就直接來看 ServiceMethod 是如何被創(chuàng)建的吧。
ServiceMethod.Builder
發(fā)現(xiàn) ServiceMethod 也是通過建造者模式來創(chuàng)建對象的。那就進入對應構造方法:
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 接口方法的注解
this.methodAnnotations = method.getAnnotations();
// 接口方法的參數(shù)類型
this.parameterTypes = method.getGenericParameterTypes();
// 接口方法參數(shù)的注解
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
在構造方法中沒有什么大的動作,那么就單刀直入 build() 方法:
public ServiceMethod build() {
// 根據(jù)接口方法的注解和返回類型創(chuàng)建 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?");
}
// 根據(jù)之前泛型中的類型以及接口方法的注解創(chuàng)建 ResponseConverter
responseConverter = createResponseConverter();
// 根據(jù)接口方法的注解構造請求方法,比如 @GET @POST @DELETE 等
// 另外還有添加請求頭,檢查url中有無帶?,轉化 path 中的參數(shù)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
// 若無 body 則不能有 isMultipart 和 isFormEncoded
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).");
}
}
// 下面的代碼主要用來解析接口方法參數(shù)中的注解,比如 @Path @Query @QueryMap @Field 等等
// 相應的,每個方法的參數(shù)都創(chuàng)建了一個 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 使用。
基本上做的事情就是:
- 創(chuàng)建 CallAdapter ;
- 創(chuàng)建 ResponseConverter;
- 根據(jù) API 接口方法的注解構造網(wǎng)絡請求方法;
- 根據(jù) API 接口方法參數(shù)中的注解構造網(wǎng)絡請求的參數(shù);
- 檢查有無異常;
代碼中都是注釋,在這里就不詳細多講了。
ServiceMethod serviceMethod = loadServiceMethod(method); 這句代碼我們看完了,那么看接下來的 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); 。
OkHttpCall
在 OkHttpCall 的構造器中沒什么大動作,搞不了大事情的:
OkHttpCall(ServiceMethod<T> serviceMethod, Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
而真正搞事情的是 serviceMethod.callAdapter.adapt(okHttpCall); 這句代碼。
ExecutorCallAdapterFactory
在 Retrofit 中默認的 callAdapterFactory 是 ExecutorCallAdapterFactory 。我們就進入它的 get(Type returnType, Annotation[] annotations, Retrofit retrofit) 看看吧,返回了一個匿名類 CallAdapter<Object, Call<?>> ,在其中有 adapt(Call<Object> call) 方法:
@Override
public 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);
}
};
}
可以看到它 return new ExecutorCallbackCall<>(callbackExecutor, call); 。ExecutorCallbackCall 是實現(xiàn)了 retrofit2.Call ,這里注意下,是 Retrofit 中的 Call 而不是 OkHttp 中的 Call 。使用了裝飾者模式把 retrofit2.Call 又包裝了一層。
在得到了 ExecutorCallbackCall ,我們可以調用同步方法 execute() 或異步方法 enqueue(Callback<T> callback) 來執(zhí)行該 call 。
ExecutorCallAdapterFactory.ExecutorCallbackCall
那我們就跟進同步方法 execute() 吧,異步的 enqueue(Callback<T> callback) 就不看了。了解過 OkHttp 的同學應該都知道這兩個方法的區(qū)別,就是多了異步執(zhí)行和回調的步驟而已。
@Override
public Response<T> execute() throws IOException {
// delegate 就是構造器中傳進來的 OkHttpCall
return delegate.execute();
}
所以,其實就是調用了 OkHttpCall 的 execute() 方法。
所以我們又要回到 OkHttpCall 中了。
OkHttpCall
@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 {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
// 根據(jù) serviceMethod 中的眾多數(shù)據(jù)創(chuàng)建出 Okhttp 中的 Request 對象
// 注意的一點,會調用上面的 ParameterHandler.apply 方法來填充網(wǎng)絡請求參數(shù)
// 然后再根據(jù) OkhttpClient 創(chuàng)建出 Okhttp 中的 Call
// 這一步也說明了在 Retrofit 中的 OkHttpCall 內部請求最后會轉換為 OkHttp 的 Call
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
// 檢查 call 是否取消
if (canceled) {
call.cancel();
}
// 執(zhí)行 call 并轉換響應的 response
return parseResponse(call.execute());
}
在 execute() 做的就是將 Retrofit 中的 call 轉化為 OkHttp 中的 call 。
最后讓 OkHttp 的 call 去執(zhí)行。
至此,Retrofit 的網(wǎng)絡請求部分源碼已經全部解析一遍了。
剩下的就是響應部分了,趁熱打鐵。
響應源碼解析
我們可以看到 OkHttpCall.execute() 中的最后一句:parseResponse(call.execute())。
所以對響應的處理就是 parseResponse(okhttp3.Response rawResponse) 這個方法了。
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();
// 如果返回的響應碼不是成功的話,返回錯誤 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) {
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;
}
}
在 parseResponse(okhttp3.Response rawResponse) 中主要是這句代碼:
T body = serviceMethod.toResponse(catchingBody);
將 ResponseBody 直接轉化為了泛型,可以猜到這也是 Converter 的功勞。
ServiceMethod
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
果然沒錯,內部是調用了 responseConverter 的。
BuiltInConverters
BuiltInConverters 中有好幾種內置的 Converter 。并且只支持返回 ResponseBody 。我們來看下它們的實現(xiàn):
static final class StreamingResponseBodyConverter
implements Converter<ResponseBody, ResponseBody> {
static final StreamingResponseBodyConverter INSTANCE = new StreamingResponseBodyConverter();
@Override
public ResponseBody convert(ResponseBody value) throws IOException {
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();
}
}
}
其實說白了就是將 ResponseBody 轉化為對應的數(shù)據(jù)類型了。比如在 GsonConverterFactory 中就是把 ResponseBody 用 gson 轉化為對應的類型,有興趣的同學可以自己看下。這里也沒什么神秘的,相信大家都懂的。
到這里就把 Retrofit 響應部分的源碼解析完畢了。
大家自行消化一下吧。
我自己也寫得頭暈了。。。笑 cry
Footer
最后,相信大家已經了解了 Retrofit 到底是怎么一回事了。
Retrofit 內部訪問網(wǎng)絡仍然是通過 OkHttp ,而只是把構造請求和響應封裝了一下,更加簡單易用了。
還有,看過框架源碼的都知道在源碼中有很多設計模式的體現(xiàn),比如建造者模式、裝飾者模式以及 OkHttp 中的責任鏈模式等。這些也正是值得我們學習的地方。
好啦,今天結束了。如果有問題的同學可以留言咯。
Goodbye