Retrofit原始碼解析
在學習了Retrofit分析-漂亮的解耦套路(視訊版)後,自己又仔細的鑽研了一下Retrofit的原始碼,也大致清楚了Retrofit進行網路請求的步驟。好記性不如爛筆頭,以文章的形式將對於Retrofit的思考記錄下來,也加深理解。(如有錯誤,請不吝賜教)
分析原始碼的一般姿勢
首先我們聊一下對於一個優秀的libary,應該怎樣一步一步地分析它。在上面的視訊中,介紹了一個很好的方法。分為三個階段:
- what: 這個框架是用來幹什麼的,暴露給使用者的API都是用來幹嘛的。
- how: 這個API內部是怎樣實現的,整個框架正常呼叫時內部是一個怎樣的流程。
- why: 為什麼要這樣實現,有什麼好處,可不可以有其他的實現,與原來的實現相比怎麼樣。
通過了解、深入、思考,將整個框架一層一層地剖析出來,由淺入深,最後從全域性再看這個框架,或許我們會不自禁的讚歎程式碼的美妙。
Retrofit使用(what)
詳細的瞭解Retrofit使用及API可以到Retrofit的Github。
簡單使用
首先,我們需要建立一個請求介面,內部是一個請求方法,通過註解的方式定義請求型別及url
public interface IHttpRequestTest {
@GET("api/data/福利/{number}/{page}")
Call<BaseModel<ArrayList<Benefit>>> getBenefits(@Path("number") int number, @Path("page") int page);
}
複製程式碼
然後我們就可以愉快地使用Retrofit。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class);
Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2);
call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {
@Override
public void onResponse(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, @NonNull Response<BaseModel<ArrayList<Benefit>>> response) {
if(response.body() != null && response.body().results != null){
myAdapter.setData(response.body().results);
}
}
@Override
public void onFailure(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, Throwable t) {
Toast.makeText(MainActivity.this, "請求失敗:" + t.getMessage(),Toast.LENGTH_SHORT).show();
}
});
複製程式碼
一般http的請求步驟
如上圖,對於一般http請求:- 生成request請求,包括請求型別,url等等,放入Excutor佇列
- 請求在Excutor中迴圈進行httpcall請求
- 等待請求回撥
接下來,我們就可以根據這個一般的http請求步驟,通過debug的形式弄清楚Retrofit的內部邏輯。
Retrofit原始碼解析
探索Retrofit物件初始化時引數的真正含義
首先第一步,就是生產Retrofit物件,並傳入基本的引數。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/")
.addConverterFactory(GsonConverterFactory.create())
.build();
複製程式碼
進入Retrofit類的原始碼,可以看到build方法
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//如果callbackExcutor為空,建立預設的callbackEcxutor
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
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,將使用預設的converterFactories
converterFactories.addAll(platform.defaultConverterFactories());
//返回Retrofit物件,包括在Builder中新增的自定義引數。
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
複製程式碼
可以看到,生成的retrofit物件有五個引數必須要存在。
baseUrl
就不用多說了,是必須的。callFactory
使用的是OkHttp3中的OkHttpClient
。callbackExcutor
與callAdapterFactory
中的callAdapter
都對應一般請求步驟中的Excutor
,只不過callbackExcutor
用於在請求內部回撥中切換執行緒,回撥的方法存在哪個執行緒中取決於callbackExcutor
在哪個執行緒。比如請求要在子執行緒中,回撥的方法在主執行緒中。callAdapter
是真正的請求物件,callbackExcutor
相當於在callAdapter
中切換執行緒。converterFactory
是用來解析返回的json資料的。當然這些具體的解釋在下面的分析中都會看到。
有原始碼可看出,預設的callbackExcutor
、callAdapterFactory
、converterFactory
新增都與platform
物件有關。這是一個Platform
類,進入研究一下。
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();
}
@Nullable Executor defaultCallbackExecutor() {return null;}
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {...}
List<? extends Converter.Factory> defaultConverterFactories() {...}
@IgnoreJRERequirement // Only classloaded and used on Java 8.
static class Java8 extends Platform {...}
static class Android extends Platform {
@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 List<? extends Converter.Factory> defaultConverterFactories() {
return Build.VERSION.SDK_INT >= 24
? singletonList(OptionalConverterFactory.INSTANCE)
: Collections.<Converter.Factory>emptyList();
}
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
}
}
複製程式碼
內容有點多,這裡我只提取了對現在有用的。整體來看,Platform
內部有兩個子類繼承了該類Android與Java8
。還有一個get方法,獲取Platform靜態例項,靜態例項呼叫findPlatform()方法。這個方法是根據程式所執行的環境決定是返回一個Android
例項還是Java8
例項或者Platform
例項。這裡我們的環境是Android
,所以具體看Android
類中的方法。
- defaultCallbackExecutor()方法,返回了
MainThreadExecutor
類例項。此類在Android類內部,可以清晰的看出其中的handler靜態變數是用的主執行緒的looper。也就是說,預設的callbackExcutor
是執行在主執行緒的。 - defaultCallAdapterFactories()方法,引數是上面得到的
callbackExcutor
。方法內部建立了ExecutorCallAdapterFactory
類例項,並返回該例項的List。 - defaultConverterFactories()方法,返回了
OptionalConverterFactory
例項,此類中的轉換converter()
方法只檢查了引數是否為空就將其返回了,說明預設的回撥資料是不進行任何轉換的。
至此,三個預設引數已確定。
獲得自定義介面物件
IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class);
這是接下來的執行步驟,自定義介面物件由retrofit類中的create方法建立,進去探討一下。
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 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);
}
});
}
複製程式碼
該方法返回的是通過反射獲得自定義介面的代理類,並在類中重寫了invoke方法。這表明當該類中的方法被執行時,就會攔截此方法。
攔截並解析方法。
Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2);
由上一步驟可知,當執行代理類中的方法時,會被攔截。並最終執行loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
方法並返回。先看一下loadServiceMethod(method)
方法。
ServiceMethod<?> loadServiceMethod(Method method) {
//serviceMethodCache,如果執行過此方法,就會有快取存在這裡,直接取出即可
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;
}
複製程式碼
嗯...,除了前面與快取有關的東西,又跑到了ServiceMethod
類中的parseAnnotations(this, method)
方法裡。
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 T invoke(Object[] args);
}
複製程式碼
這是一個抽象類,第一句RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
,是解析方法構建request引數的。RequestFactory類中內容比較多,簡單講一下就是通過獲得方法註解、返回型別、方法引數,分別進行解析。
最後它又跑到HttpServiceMethod
類中了。此類繼承了ServiceMethod
類。
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);
}
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
}
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
return retrofit.responseBodyConverter(responseType, annotations);
}
複製程式碼
簡化了異常檢測類程式碼,可以看到最後返回的是HttpServiceMethod
類的例項物件,並將retrofit中的一些預設引數傳了進去。
解析完了loadServiceMethod(method)
,接下來就是執行invoke方法了。由於loadServiceMethod(method)
返回的是HttpServiceMethod
類的例項物件,所以執行的就是HttpServiceMethod
類中的invoke方法。
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
複製程式碼
此方法很簡單,呼叫了callAdapter中的adapt方法,引數是一個OkHttpCall,字面意思就是將OkHttpCall
適配為其他類。那麼這個類是什麼什麼呢。應該是我們需要返回的Call<BaseModel<ArrayList<Benefit>>>
類。那麼怎麼適配的呢。上面我們已經說到,retrofi中的預設引數callAdapterFactories
是ExecutorCallAdapterFactory
類,而callAdapterFactories
中的callAdapter
是通過callAdapterFactories.get()
獲得的,那麼就要去這裡面找了。
@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);
}
};
}
複製程式碼
返回的CallAdapter
物件就是我們需要的。其中重寫了adapt方法,也是我們要找的。它又返回了ExecutorCallbackCall
物件,第一個引數就是預設執行在主執行緒中的callbackExecutor
,第二個引數則是呼叫時傳進來的OkHttpCall
物件。所以說我們需要返回的Call<BaseModel<ArrayList<Benefit>>>
物件就是ExecutorCallbackCall
物件。
依靠OkHttp3進行網路請求,轉換回撥函式執行緒到主執行緒。
call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {...});
,終於到最後一步了,拿著上一步返回的Call<BaseModel<ArrayList<Benefit>>>
物件進行非同步請求。呼叫了enqueue方法,也就是ExecutorCallbackCall
中的enqueue方法。
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);
}
});
}
});
}
複製程式碼
構造方法給了出來,變數名有些轉換。callbackExecutor
依然是在主執行緒中的那個callbackExecutor
,delegate
是上一步傳進來的OkHttpCall
物件。而enqueue的引數callback則是我們自己傳入的callback。
方法中首先是delegate
也就是OkHttp3進行的非同步請求,在回撥的函式中,通過callbackExecutor
使執行緒切換為主執行緒,再呼叫我們自己的callback中對應的函式,使我們可以再回撥中進行操作,並且是在主執行緒中。
OkHttpCall
這個類就不在多講了,主要作用是retrofit通過OkHttp3進行請求的一個轉換。
結語
第一次分析原始碼,雖然有視訊的幫助,但許多類之間跳來跳去的讓人眼花繚亂,很艱難地順了下來。有了這樣一個大概的瞭解,接下來就要分析其設計模式,欣賞其漂亮的解耦套路。當然,如果你已經有了上面的瞭解,接下來看Retrofit分析-經典設計模式案例就會簡單明瞭。