Android Retrofit原始碼分析
作為我最愛的一個網路請求庫,怎麼不來搞搞它呢?
其實Retrofit是OKHTTP的一個封裝,它的網路請求還是交給OKHTTP來做的。
我們一般情況下,會這樣來做例項化Retrofit:
Retrofit client = new Retrofit.Builder()
.baseUrl("xxxxx")
.addConverterFactory(GsonConverterFactory.create())
.build();
這裡使用的是建造者模式,什麼是建造者模式呢?將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
具體可以參考:
然後我們來看看我們的Retrofit:
首先建立了一個Retrofit.Builder物件,該類是Retrofit的靜態內部類:
public static final class Builder {
private final Platform platform;
//網路請求器的工廠,用來產生網路請求其(Call)
private @Nullable okhttp3.Call.Factory callFactory;
//網路請求的url地址
private HttpUrl baseUrl;
//資料轉換工廠
private final List<Converter.Factory> converterFactories = new ArrayList<>();
//網路請求介面卡工廠
private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
//回撥方法執行器
private @Nullable Executor callbackExecutor;
//標誌位,是否提前對業務介面中的註解進行驗證轉換的標誌位
private boolean validateEagerly;
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.
//新增資料轉換工廠
converterFactories.add(new BuiltInConverters());
}
public Builder() {
//Platform.get()返回一個Platform物件,指定執行平臺
this(Platform.get());
}
... ...
}
在方面的程式碼中,我們是首先會去找到Retrofit執行的平臺:
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();
}
對於我們安卓來說,肯定是返回Android例項了。
然後讓converterFactories
添加了一個BuiltInConverters
物件,它是一個內建的轉換工廠,它禁止重寫它的任何方法,但是這樣可以保證當使用converters的正確行為。
Builder物件構建好了後,呼叫了baseUrl
方法:
public Builder baseUrl(String baseUrl) {
//檢查baseUrl不為null
checkNotNull(baseUrl, "baseUrl == null");
//將它轉換為一個HttpUrl物件
//在這裡會檢查baseUrl的正確性
HttpUrl httpUrl = HttpUrl.parse(baseUrl);
if (httpUrl == null) {
throw new IllegalArgumentException("Illegal URL: " + baseUrl);
}
return baseUrl(httpUrl);
}
繼續:
public Builder baseUrl(HttpUrl baseUrl) {
checkNotNull(baseUrl, "baseUrl == null");
//把URL引數分割成幾個路徑碎片
List<String> pathSegments = baseUrl.pathSegments();
//檢測最後一個碎片以檢查URL引數是不是以/結尾
//不是就丟擲異常。
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
//給baseUrl賦值
this.baseUrl = baseUrl;
return this;
}
新增完baseUrl後,我們就會新增資料轉換工廠:addConverterFactory(GsonConverterFactory.create())
首先我們看一下GsonConverterFactory
的建立:
public static GsonConverterFactory create() {
return create(new Gson());
}
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static GsonConverterFactory create(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
return new GsonConverterFactory(gson);
}
private GsonConverterFactory(Gson gson) {
this.gson = gson;
}
可以看到,它建立了一個Gson物件,將Gson物件封裝起來了。
/** Add converter factory for serialization and deserialization of objects. */
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(checkNotNull(factory, "factory == null"));
return this;
}
上面就是給converterFactories添加了一個factory。
接下來,呼叫build
方法,建造Retrofit物件:
public Retrofit build() {
//必須要有baseUrl
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//網路請求執行器
//如果沒有callFactory,預設使用OKHttpClient
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));
//在Builder構造的時候,添加了一個BuildInConverters()
//資料轉換工廠集合
// Make a defensive copy of the converters.
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
//建立Retrofit物件
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
接下來,我們來看看Retrofit的構造:
public final class Retrofit {
// 網路請求配置物件(對網路請求介面中方法註解進行解析後得到的物件)
// 作用:儲存網路請求相關的配置,如網路請求的方法、
//資料轉換器、網路請求介面卡、網路請求工廠、基地址等
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> adapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
@Nullable Executor callbackExecutor, boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
//只可以讀,不能寫
this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
//只可以讀,不能寫
this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
... ...
}
可以看到,Retrofit中的成員變數和Builder中的差不多,在構造Retrofit物件的時候,進行賦值。
當我們構造好了Retrofit後,我們會像這樣進行網路請求,以get請求為例:
public interface GetService {
@GET("book/{id}")
Call<String> getRequest(@Path("id") int id);
}
GetService service = client.create(GetService.class);
Call<String> call = service.getRequest(1);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
}
@Override
public void onFailure(Call<String> call, Throwable t) {
}
});
首先我們呼叫create
方法得到GetService 物件:
在這是, Retrofit是通過外觀模式&代理模式,使用create方法建立網路請求介面例項:
public <T> T create(final Class<T> service) {
//驗證service是interface
Utils.validateServiceInterface(service);
if (validateEagerly) {
//判斷是否需要提前驗證
//預設為false
eagerlyValidateMethods(service);
}
//建立網路請求介面的動態代理物件,即通過動態代理建立網路請求介面例項
//該動態代理主要是為了拿到網路請求介面例項上的所有註解
return (T) Proxy.newProxyInstance(
service.getClassLoader(), //動態生成介面實現類
new Class<?>[] { service }, //動態建立例項
new InvocationHandler() { //代理類的方法的呼叫會交給它的invoke方法
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {
//proxy是代理類物件
//method是呼叫的代理類的哪個方法
//args是方法的餐忽
// 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<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
在上面的程式碼中,我們可以看到的是,主要是呼叫了Proxy.newProxyInstance
方法,在這裡主要使用到了動態代理,什麼是動態代理呢?代理類在程式執行時建立的代理方法被稱為動態代理,也就是說,在這種情況下,代理類並不是在Java程式碼中定義的,而是在執行時根據我們在Java程式碼中的“指示”動態生成的。
關於動態代理,可以參考:https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a
我們先看看Proxy.newProxyInstance
方法中做了什麼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
//查詢或生成指定的代理類
Class<?> cl = getProxyClass0(loader, intfs);
//得到cl的構造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//檢查cl的訪問許可權
if (!Modifier.isPublic(cl.getModifiers())) {
// Android-changed: Removed AccessController.doPrivileged
cons.setAccessible(true);
}
//返回它的例項
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
在這裡,通過Proxy工具,為委託類的介面自動生成一個代理物件,後續的方法呼叫都是這個代理物件進行發起的,最終會執行到InvocationHandler#invoke
方法。
然後我們看看這裡InvocationHandler#invoke
方法的實現:
@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);
}
//關注1
//讀取網路請求接口裡的方法,並根據前面的配置好的屬性配置serviceMethod物件
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
//關注2
//根據配置好的serviceMethod物件建立OkHttpCall物件
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//關注點3
//呼叫OkHttp,並根據oKHttpCall返回Call(不考慮RxJava)
return serviceMethod.callAdapter.adapt(okHttpCall);
}
我們先看關注點1:
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
我們先看看看ServiceMethod是什麼:
Adapts an invocation of an interface method into an HTTP call
將介面方法的呼叫調整為一個HTTP call
也就是哦說,一個serviceMethod物件對應於網路請求接口裡的一個方法
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache
= new ConcurrentHashMap<>();
ServiceMethod<?, ?> loadServiceMethod(Method method) {
//在建立ServiceMethod物件前,先看serviceMethodCach有沒有快取之前建立過的網路請求
ServiceMethod<?, ?> result = serviceMethodCache.get(method);
if (result != null) return result;
//設定同步鎖
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//建立ServiceMethod例項
result = new ServiceMethod.Builder<>(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
然後我們看看ServiceMethod的建立:
首先是new ServiceMethod.Builder<>(this, method)
:
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() {
//根據網路請求介面方法的返回值和註解型別,從Retrofit物件中獲取對應的網路請求介面卡
callAdapter = createCallAdapter();
//獲得該網路介面卡返回的資料型別
//如Call<String>
responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {