04、Android--Retrofit原理解析
Retrofit原理
Retrofit的建立過程
當我們使用Retrofit請求網路時,首先要寫請求介面:
public interface ApiService {
@GET("getInfo.php?ip=59.105.23.12")
Call<IpModel> getIpMsg();
}
接著,我們通過呼叫如下程式碼來建立Retrofit:
Retrofit retrofit = new Retrofit.Builder() .baseUrl(url) .addConverterFactory(GsonConverterFactory.create()) .build();
Retrofit是通過建造者模式構建出來的。接下來檢視Builder方法:
public Builder() {
this(Platform.get());
}
檢視Platform的get方法,如下所示:
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 Platform.Android(); } } catch (ClassNotFoundException ignored) { } try { Class.forName("java.util.Optional"); return new Platform.Java8(); } catch (ClassNotFoundException ignored) { } try { Class.forName("org.robovm.apple.foundation.NSObject"); return new Platform.IOS(); } catch (ClassNotFoundException ignored) { } return new Platform(); }
Platform的get方法最終呼叫的是findPlatform方法,根據不同的執行平臺來提供不同的執行緒池。
接下來檢視build方法,程式碼如下所示
public Retrofit build() { // baseUrl 是必須指定的 if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } // callFactory 預設為this.callFactory okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { // 如果沒有設定callFactory,則直接建立 OkHttpClient callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { // 重點標記 callbackExecutor = platform.defaultCallbackExecutor(); } // adapterFactories主要用於儲存對Call進行轉化的物件 List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // 的converterFactories主要用於儲存轉化資料物件 List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
this.callFactory就是我們在構建Retrofit時呼叫callFactory方法所傳進來的,如下所示:
public Retrofit.Builder callFactory(okhttp3.Call.Factory factory) {
this.callFactory = checkNotNull(factory, "factory == null");
return this;
}
因此,如果需要對 OkHttpClient 進行設定,則可以構建 OkHttpClient 物件,然後呼叫callFactory方法將
設定好的OkHttpClient傳進去。
Call的建立過程
下面我們建立Retrofit例項並呼叫如下程式碼來生成介面的動態代理物件:
ApiService apiService = retrofit.create(ApiService.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();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// method就是我們定義的getIpMsg方法。
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 建立OkHttpCall
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
// 建立 ExecutorCallbackCall,並傳入OkHttpCall
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
可以看到 create 方法返回了一個 Proxy.newProxyInstance 動態代理物件。
當我們呼叫IpService的 getIpMsg方法時,最終會呼叫InvocationHandler的invoke方法。它有三個引數:第一個是代理物件,第二個是呼叫的方法,第三個是方法的引數。
下面檢視loadServiceMethod方法:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod 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;
如果沒有,就建立一個,並加入 serviceMethodCache 快取起來。
下面看ServiceMethod是如何構建的,程式碼如下所示:
public ServiceMethod build() {
// 最終會得到我們在構建Retrofit呼叫build方法時 adapterFactories新增的物件的get方法
callAdapter = createCallAdapter();
// CallAdapter的responseType得到的是返回資料的真實型別
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?");
}
// 遍歷converterFactories列表中儲存的Converter.Factory,並返回合適的Converter用來轉換物件。
responseConverter = createResponseConverter();
// 遍歷parseMethodAnnotation方法來對請求方式(比如GET、POST)和請求地址進行解析。
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);
}
// 對方法中的引數註解進行解析(比如@Query、@Part)。
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
if (parameterAnnotations == null) {
throw parameterError(p, "No Retrofit annotation found.");
}
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
......
// 建立ServiceMethod類並返回。
return new ServiceMethod<>(this);
}
在前面Retrofit的build方法中,adapterFactories 列表預設會新增 defaultCallAdapterFactory。
CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
if (callbackExecutor != null) {
return new ExecutorCallAdapterFactory(callbackExecutor);
}
return DefaultCallAdapterFactory.INSTANCE;
}
defaultCallAdapterFactory指的是ExecutorCallAdapterFactory。 ExecutorCallAdapterFactory的get方法如下所示:
@Override
public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
// 返回真實資料型別ipModel
@Override public Type responseType() {
return responseType;
}
// adapt 方法會建立ExecutorCallbackCall,它會將call 的回撥轉發至UI執行緒。
@Override public <R> Call<R> adapt(Call<R> call) {
return new ExecutorCallAdapterFactory.ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
get方法會得到CallAdapter物件,CallAdapter的responseType方法會返回資料的真實型別。
檢視Retrofit的create方法,在呼叫了loadServiceMethod方法後會建立OkHttpCall,OkHttpCall 的構造方法只是進行了賦值操作。緊接著呼叫 serviceMethod.callAdapter.adapt(okHttpCall)。
其中,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;
}
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("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()) {
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);
}
});
}
});
}
......
}
可以看出ExecutorCallbackCall是對Call的封裝,它主要添加了通過callbackExecutor將請求回撥到 UI 線
程。當我們得到 Call 物件後會呼叫它的 enqueue 方法,其實呼叫的是ExecutorCallbackCall的enqueue方法。
而從上面程式碼註釋處可以看出ExecutorCallbackCall的enqueue方法最終呼叫的是delegate的enqueue方法。
delegate是傳入的OkHttpCall。
Call的enqueue方法
下面我們就來檢視OkHttpCall的enqueue方法,程式碼如下所示:
@Override public void enqueue(final Callback<T> callback) {
if (callback == null) throw new NullPointerException("callback == null");
okhttp3.Call call;
Throwable failure;
......
// 呼叫了okhttp3.Call型別的call的enqueue方法
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);
}
......
});
}
我們再來看看parseResponse方法:
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
......
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) {
return Response.success(null, rawResponse);
}
OkHttpCall.ExceptionCatchingRequestBody catchingBody = new OkHttpCall.ExceptionCatchingRequestBody(rawBody);
try {
// 重要標記
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
根據返回的不同狀態碼code值來做不同的操作。接下來 看toResponse方法:
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
這個responseConverter就是此前講過在ServiceMethod的build方法呼叫createResponseConverter方法返回
的 Converter。
在此前的例子中我們傳入的是 GsonConverterFactory,因此可以檢視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));
return new GsonResponseBodyConverter<>(gson, adapter);
}
......
}
在GsonConverterFactory中有一個方法responseBodyConverter,它最終會建立GsonResponse- BodyConverter:
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
在GsonResponseBodyConverter的convert方法裡會將回調的資料轉換為JSON格式。
此前呼叫responseConverter.convert是為了轉換為特定的資料格式。Call的enqueue方法主要做的就是用OkHttp來請求網路,將返回的Response進行資料轉換並回調給UI執行緒。