目錄
前言
怎樣用Retrofit請求網絡
Retorfit是如何創建的
請求接口的實例是如何創建出來的
Call到底是什么
5.1 ServiceMethod的創建
5.2 Call的創建
5.3 總結
Call的enqueue方法到底干了什么
遺留
1.前言
近些天回想起工作這幾年,感覺一事無成。俗話說,就算是咸魚,也要做一條有理想的咸魚。因此,訂了一個長期的計劃:博客。
為何選擇Retrofit作為人生中的第一篇博客?
- Retrofit太火了,在最新的2017Android框架中Retrofit排名第一
- Retrofit是對OkHttp的封裝。作為一個封裝庫,它用了大量的設計模式,有很多值得借鑒的地方
- 代碼量少,相對簡單
在讀這篇文章前,假設你已經掌握了
- Retrofit如何使用
分析源碼一般都有一根主線,本文打算從Retrofit最基本的用法開始,一步一步深入了解Retrofit全貌。
2.怎樣用Retrofit請求網絡
一般我們使用Retrofit需要以下五步:
- 定義請求接口
- 創建Retrofit實例
- 獲取請求接口的實例
- 獲取Call對象
- 同步或異步請求
//1.定義請求接口
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
//2.創建Retrofit實例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
//3.通過Retrofit實例獲取請求接口實例(動態代理)
GitHubService service = retrofit.create(GitHubService.class);
//4.獲取Call對象
Call<List<Repo>> call = service.listRepos("octocat");
//5.異步請求
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//doSometionSuccess
...
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
//doSometionError
...
}
});
我們將會以上述代碼為導火索來分析源碼。針對上述代碼我們先提出以下幾個問題,分別對應代碼注釋中的2 3 4 5對應的代碼:
- Retrofit是如何創建的
- 請求接口的實例是如何創建出來的
- Call到底是什么
- Call的enqueue方法到底干了什么
我會在每節的開始處附上每個問題對應的代碼
3.Retorfit是如何創建的
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
只要稍微了解Build模式的童鞋都能一眼看出來,Retrofit是通過Build模式構建的。關于Retrofit中的那些設計模式,我打算另起篇幅介紹,這里就不敘述了。
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
private Executor callbackExecutor;
private boolean validateEagerly;
private final Platform platform;
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
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);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
這里我先簡單介紹一下各個參數的作用:
- callFactory,前面介紹了Retrofit是對OkHttp的封裝,callFactory是Okhttp3提供的類,用來自定義OkHttpClient()。
- callbackExecutor,請求完成后,用來對回調過程進行線程控制。默認是在主線程中回調。
- adapterFactories,它是CallAdapter.Factory的集合。CallAdapter.Factory是一個工廠,用來生產callAdapter,callAdapter是對OkHttpCall的適配。至于OkHttpCall是什么,我們會在下面的篇幅中會介紹。隨便提一句,Retrofit集成RxJava就是通過CallAdapter.Factory實現的。
- converterFactories,它與adapterFactories類似。不同的是,它生產的Converter可以控制請求結果的解析。例如,進行JSON還是XML解析。
- validateEagerly默認為false,validateEagerly參數如果我們在配置的時候設置為true的時候,程序是在做一個預處理的操作,會提前解析請求接口中定義的方法,生產MethodHandler并緩存。
- Platform,Retrofit希望是一個跨平臺的網絡請求庫,因此它定義了Platform接口。每個平臺都提供了默認的callbackExecutor與CallAdapter.Factory實現。
platform源碼
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);
}
}
4.請求接口的實例是如何創建出來的
GitHubService service = retrofit.create(GitHubService.class);
我們看一下Retrofit的create方法。
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//動態代理,返回的是T類型的對象。
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 = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
我們先暫時不去分析eagerlyValidateMethods(service)方法,因為它看起來即使validateEagerly為false,我們的程序仍能正常運行。
Proxy.newProxyInstance()生成了一個動態代理對象,而InvocationHandler可以攔截這個對象的所有方法。這么說可能有些抽象,你可以簡單的認為以下這兩段代碼等價。
GitHubService service = retrofit.create(GitHubService.class)
GitHubService service = new GitHubService() {
//創建InvocationHandler對象
InvocationHandler invocationHandler = 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 = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
};
//調用listRepos方法其實是調用InvocationHandler的invoke方法,參數分別為:當前GitHubService對象,listRepos()方法對象,listRepos()傳入的參數
@Override
public Call<List<String>> listRepos(@Path("user") String user) {
return invocationHandler.invoke(this, GitHubService.class.getMethod("listRepos"), user);
}
}
5.Call到底是什么
Call<List<Repo>> call = service.listRepos("octocat");
service.listRepos()方法其實調用了invocationHandler的invoke()方法。其核心代碼僅有三行。
//ServiceMethod的創建
ServiceMethod serviceMethod = loadServiceMethod(method);
//Call的創建
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
這部分將分為兩部分介紹,ServiceMethod的創建和Call的創建。
5.1 ServiceMethod的創建
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是為了創建ServiceMethod,然后將其放入的緩存中,緩存的Key為Method對象。ServiceMethod同樣是通過Builder模式創建的。上述方法中的this指的是當前Retrofit對象。因此可以推測ServiceMethod.Builder持有了Retrofit對象和Method對象的引用。事實也的確如此,如下:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
這段代碼主要是保存了Retrofit的實例對象,method對象,method上的注解,method參數注解,method參數類型。
public ServiceMethod build () {
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];
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);
}
createCallAdapter主要是通過Builder中保存的retrofit對象,找到CallAdapterFactory集合,遍歷集合,根據Method的返回值類型和Method上的注解,找到合適的callAdapter。callAdapter是一個接口,定義了兩個方法:Type responseType(),返回值類型;<R> T adapt(Call<R> call),用來返回代理對象。我們看一下Platform中默認提供的CallAdapterFactory。
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) {
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);
}
};
}
}
這樣ServiceMethod內部就創建了一個匿名的CallAdapter對象。
createResponseConverter與createCallAdapter過程類似,不再贅述。build()方法中其余的代碼主要是通過解析注解和拼接參數來生成Http請求數據,如:get、post、delete等方法、請求Url、參數。至此,ServiceMethod中擁有了callAdapter、responseConverter以及和網絡請求有關的url、parameter、method等。loadServiceMethod介紹完畢。
5.2 Call的創建
我們看一下這兩段代碼的意思
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
OkHttpCall是Call的實現類,它有一個ServiceMethod對象和一個請求參數的數組。如下:
final class OkHttpCall<T> implements Call<T> {
//構造方法
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
this.serviceMethod = serviceMethod;
this.args = args;
}
}
我們在 ServiceMethod的創建 中介紹了serviceMethod.callAdapter是一個匿名類。它的adapt方法返回了一個ExecutorCallbackCall對象。
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
這是典型的裝飾器模式,ExecutorCallbackCall是對OkHttpCall對象的裝飾。它內部的delegate對象就是上面分析的OkHttpCall對象。callbackExecutor是我們在** Retorfit是如何創建的 **中介紹的callbackExecutor對象。Platform提供了默認實現MainThreadExecutor。MainThreadExecutor很簡單,它將接收到的Runnable對象交給主線程的Handler去處理。
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
5.3 總結
我們返回的call對象其實是一個ExecutorCallbackCall對象,它實現了Call接口,是對OkHttpCall的裝飾,并且內部持有了callbackExecutor對象。OkHttpCall也實現了Call接口,它有持有了一個ServiceMethod對象和一個請求參數的數組的引用。ServiceMethod封裝了請求的細節,如get、post、delete等方法、請求Url、參數等,同時callAdapter和responseConverter也是在ServiceMethod中創建的。
6.Call的enqueue方法到底干了什么
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
//doSometionSuccess
...
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
//doSometionError
...
}
});
在上節中,我們知道了返回的Call實例是ExecutorCallbackCall類型的對象。那我們看一下ExecutorCallbackCall的enqueue()方法。
public void enqueue(final Callback<T> callback) {
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(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
}
}
});
}
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
}
});
}
});
}
我們先來介紹一下這段代碼中出現的變量,然后再來分析整段代碼的含義。
- delegate是上一節介紹的OkHttpCall對象。
- callbackExecutor是上一節介紹的MainThreadExecutor對象。
這段代碼更能說明ExecutorCallbackCall采用了裝飾模式,它生成了一個新的Callback對象。這個新Callback有兩個作用。一,將回調切換到callbackExecutor中執行。二,增加了取消操作。最終,這個新的Callback對象交給OkHttpCall對象的enqueue方法去處理。
public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
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 {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
OkHttpCall是對Okhttp3.Call的包裝,OkHttpCall內部有一個類型為Okhttp3.Call的rawCall對象。rawCall的創建,是通過Retrofit初始化時,傳入的OkHttpClient創建的(若為空,系統提供了默認的)。在enqueue方法中利用rawCall對象發起請求。請求成功后,會通過parseResponse(rawResponse)方法對原始數據進行解析。
而parseResponse()的核心代碼只有一句話。
T body = serviceMethod.toResponse(catchingBody);
不錯,它調用了serviceMethod的toResponse方法。而serviceMethod的toResponse方法也只有一句話。
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
還記得我們在 ServiceMethod的創建 提到的,和callAdapter創建過程類似的responseConverter嗎?沒錯,就是它。
7.遺留
我們在** 請求接口的實例是如何創建出來的 ** 這節中遺留下一段代碼
if (validateEagerly) {
eagerlyValidateMethods(service);
}
我們現在回過頭來看一下這段代碼
private void eagerlyValidateMethods(Class<?> service) {
Platform platform = Platform.get();
for (Method method : service.getDeclaredMethods()) {
if (!platform.isDefaultMethod(method)) {
loadServiceMethod(method);
}
}
}
loadServiceMethod是不是很熟悉。不要告訴我你已經忘了!!! 好吧,那我來告訴你,ServiceMethod的創建 中我們最先介紹的就是這個方法。eagerlyValidateMethods是一個對service中的方法提前解析,將其緩存的過程。所以我們可以通過設置validateEagerly=true來提高請求效率。
哇,終于完了,寫博客是真的累啊
真的累啊
真累啊
累啊
累?。。?/p>