文章首發於個人我的博客:wildma的博客,這裏有更好的閱讀體驗,歡迎關注。html
最近有個想法——就是把 Android 主流開源框架進行深刻分析,而後寫成一系列文章,包括該框架的詳細使用與源碼解析。目的是經過鑑賞大神的源碼來了解框架底層的原理,也就是作到不只要知其然,還要知其因此然。java
這裏我說下本身閱讀源碼的經驗,我通常都是按照平時使用某個框架或者某個系統源碼的使用流程入手的,首先要知道怎麼使用,而後再去深究每一步底層作了什麼,用了哪些好的設計模式,爲何要這麼設計。android
系列文章:git
更多幹貨請關注 AndroidNotesgithub
(1)建立網絡請求接口:json
public interface PostmanService {
@GET("get")
Call<PostmanGetBean> testGet();
}
複製代碼
(2)建立 Retrofit 的實例:設計模式
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
複製代碼
(3)建立網絡請求接口的實例,並調用接口中的方法獲取 Call 對象:數組
PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();
複製代碼
(4)進行網絡請求緩存
Response<PostmanGetBean> response = call.execute();
複製代碼
同步請求與異步請求惟一不一樣的就是第 (4) 步,前者使用同步方法 execute(),後者使用異步方法 enqueue()。異步請求的第(4)步以下:服務器
call.enqueue(new Callback<PostmanGetBean>() {
@Override
public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
}
@Override
public void onFailure(Call<PostmanGetBean> call, Throwable t) {
}
});
複製代碼
更多 Retrofit 的使用方法能夠看我以前寫的文章 Retrofit 使用詳解 接下來咱們就根據這 4 步進行源碼閱讀。
源碼版本:2.5.0
public interface PostmanService {
@GET("get")
Call<PostmanGetBean> testGet();
}
複製代碼
這一步比較簡單,只是建立一個接口,接口中包含一個帶註解的方法。這裏沒有什麼源碼好講的,咱們放到後面結合第(3)步再一塊兒講。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://postman-echo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
複製代碼
首先咱們點擊 Retrofit 對象進去有以下常量:
/*Retrofit*/
// ServiceMethod 緩存,用來存儲網絡請求相關的配置,例如網絡請求的方法、數據轉換器、網絡請求適配器、網絡請求工廠、基地址等
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
// 網絡請求器工廠
final okhttp3.Call.Factory callFactory;
// 網絡請求的 url 地址
final HttpUrl baseUrl;
// 數據轉換器工廠的集合
final List<Converter.Factory> converterFactories;
// 網絡請求適配器工廠的集合
final List<CallAdapter.Factory> callAdapterFactories;
// 回調方法執行器
final @Nullable Executor callbackExecutor;
// 一個標誌位,用來判斷是否須要加載 ServiceMethod
final boolean validateEagerly;
複製代碼
這些常量除了 serviceMethodCache,其他均可以經過建造者模式進行配置,例如 baseUrl 能夠經過 baseUrl() 方法配置。
而後再看下 Builder() 方法:
/*Retrofit-Builder*/
Builder(Platform platform) {
this.platform = platform;
}
public Builder() {
this(Platform.get());
}
複製代碼
Builder() 方法中調用了 Platform 類的 get() 方法,get() 方法中又調用了 findPlatform() 方法,以下:
/*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) {
// Android 平臺
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional");
// Java 平臺
return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
複製代碼
能夠看到,Builder() 方法主要是用來查找當前運行的平臺,Retrofit 支持 Android 與 Java 平臺。
最後再看看 build() 方法:
/*Retrofit*/
public Retrofit build() {
//(1)
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//(2)
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//(3)
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//(4)
// Make a defensive copy of the adapters and add the default Call adapter.
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
//(5)star
// 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.addAll(platform.defaultConverterFactories());
//(5)end
//(6)
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
複製代碼
源碼中我標註了 6 個關注點,分別以下:
/*Platform*/
@Nullable Executor defaultCallbackExecutor() {
return null;
}
複製代碼
因爲運行在 Android 平臺,因此跳轉到 Platform 的實現類 Android 中的 defaultCallbackExecutor() 方法:
/*Platform-Android*/
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
複製代碼
繼續跟進到 MainThreadExecutor() :
/*Platform-Android*/
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
複製代碼
能夠看到,這裏經過建立一個主線程的 Handler 來將線程從子線程切換到主線程,這樣後面從服務器拿到數據就是在主線程了,就能夠直接進行 UI 相關操做了。
小結: 這一步使用了建造者模式去建立 Retrofit 的實例,建立的過程當中主要配置了網絡請求器工廠(callFactory)、網絡請求的 url 地址(baseUrl)、數據轉換器工廠的集合(converterFactories)、網絡請求適配器工廠的集合(callAdapterFactories)、回調方法執行器(callbackExecutor)。這裏使用建造者模式的好處是不須要知道 Retrofit 內部是怎麼建立的,只須要傳入對應的配置便可建立很是複雜的對象。
PostmanService service = retrofit.create(PostmanService.class);
Call<PostmanGetBean> call = service.testGet();
複製代碼
首先咱們點擊 create() 方法進去源碼是這樣的:
/*Retrofit*/
public <T> T create(final Class<T> service) {
// 驗證傳進來的 service(service 必須是一個接口,而且不能繼承其餘接口,不然拋出異常)
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//(1)Proxy.newProxyInstance()
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
//(2)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.
//(3)
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//(4)
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//(5)
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
複製代碼
注意:其實關注點(1)中執行 Proxy 的 newProxyInstance() 方法就已經建立了網絡請求接口的實例, create() 方法的調用並不會繼續執行關注點(2)中的 invoke() 方法。調用網絡請求接口中的方法(例如例子中 service.testGet())的時候纔會執行 invoke() 方法。
咱們繼續看例子中的第二行代碼:
Call<PostmanGetBean> call = service.testGet();
複製代碼
這行代碼的意思就是調用接口中的方法獲取 Call 對象,其實這裏調用 testGet() 方法纔是剛開始執行上面動態代理中的 invoke() 方法,因此咱們繼續分析其餘關注點吧!
咱們先點擊 loadServiceMethod() 方法進去看看:
/*Retrofit*/
ServiceMethod<?> loadServiceMethod(Method method) {
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 緩存集合中取出當前方法對應的 ServiceMethod,若是不爲空,則直接返回。若是爲空,則使用單例模式建立一個新的 ServiceMethod,即調用 parseAnnotations() 方法建立,而後再存到緩存集合中。
咱們點擊 parseAnnotations() 方法進去看看:
/*ServiceMethod*/
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//(6)
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.");
}
//(7)
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
複製代碼
/*RequestFactory*/
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
複製代碼
這裏使用了建造者模式,Builder 的構造函數只是一些簡單的賦值,以下:
/*RequestFactory-Builder*/
Builder(Retrofit retrofit, Method method) {
// Retrofit 對象
this.retrofit = retrofit;
// 方法
this.method = method;
// 方法註解
this.methodAnnotations = method.getAnnotations();
// 參數類型
this.parameterTypes = method.getGenericParameterTypes();
// 參數註解數組
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
複製代碼
繼續看看 build() 方法:
/*RequestFactory-Builder*/
RequestFactory build() {
//(6.1)
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
// 省略部分判斷...
//(6.2)
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p]);
}
// 省略部分判斷...
//(6.3)
return new RequestFactory(this);
}
複製代碼
能夠看到,關注點(6.1)中的 for 循環是用來解析方法上的註解,例如 @GET、@HTTP、@Headers 等等註解。關注點(6.2)中的 for 循環是用來解析方法上的參數的。執行到關注點(6.3)說明網絡請求接口中的註解已經解析出來了,而後建立 RequestFactory 對象並進行賦值後返回,以下:
/*RequestFactory*/
RequestFactory(Builder builder) {
// 方法,對應例子中是 testGet()
method = builder.method;
// baseUrl,對應例子中是 https://postman-echo.com/
baseUrl = builder.retrofit.baseUrl;
// http 方法,對應例子中是 GET 請求
httpMethod = builder.httpMethod;
// 相對路徑,對應例子中是 get
relativeUrl = builder.relativeUrl;
// 請求頭,沒有設置,默認爲 null
headers = builder.headers;
// contentType,沒有設置,默認爲 null
contentType = builder.contentType;
// hasBody,沒有設置,默認爲 false
hasBody = builder.hasBody;
// isFormEncoded,沒有設置,默認爲 false
isFormEncoded = builder.isFormEncoded;
// isMultipart,沒有設置,默認爲 false
isMultipart = builder.isMultipart;
// 參數數組,例子中沒有傳參數,因此爲 null
parameterHandlers = builder.parameterHandlers;
}
複製代碼
/*HttpServiceMethod*/
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) {
// 根據網絡請求接口中的返回類型與註解獲取相對應的網絡請求適配器(CallAdapter)
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
// 獲取響應類型,對應例子中是 PostmanGetBean
Type responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
// 根據網絡請求接口中的返回類型與註解獲取相對應的數據轉換器(Converter)
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
// 獲取網絡請求器工廠(okhttp3.Call.Factory)
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
複製代碼
能夠看到,該方法主要是用來獲取相對應的網絡請求適配器(CallAdapter)、數據轉換器(Converter)與網絡請求器工廠(okhttp3.Call.Factory),對應例子中分別是 ExecutorCallAdapterFactory、GsonConverterFactory 與 OkHttpClient。最後再經過這些來建立 HttpServiceMethod 對象進行返回,HttpServiceMethod 繼承自 ServiceMethod,因此能夠轉成 ServiceMethod。
loadServiceMethod() 方法拿到 ServiceMethod 對象後繼續回去看關注點(5)中剩下的:
/*Retrofit*/
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
複製代碼
這裏調用了 invoke() 方法,點擊進去是調用了 ServiceMethod 的 invoke() 方法,而後在子類 HttpServiceMethod 中實現,具體以下:
/*ServiceMethod*/
abstract T invoke(Object[] args);
/*HttpServiceMethod*/
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
複製代碼
能夠看到,這裏調用了 CallAdapter 的 adapt() 方法,參數是建立了一個 OkHttpCall 對象,OkHttpCall 對象的建立只是一些簡單的賦值,就不貼代碼了。上面已經說了,CallAdapter 對應例子中就是 ExecutorCallAdapterFactory,因此點擊 adapt() 方法進去看看:
/*ExecutorCallAdapterFactory*/
@Nullable
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
} else {
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
public Type responseType() {
return responseType;
}
// 關注點
public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call);
}
};
}
}
複製代碼
能夠看到關注點中 adapt() 方法返回的是 ExecutorCallbackCall,而 ExecutorCallbackCall 實現了 Call 接口,這樣就獲取了 Call 對象了,就能夠愉快的調用 execute() 與 enqueue() 方法進行網絡請求了。
小結:
這裏使用動態代理的好處是能夠將網絡請求接口的全部方法的調用都會集中轉發到 InvocationHandler 接口的 invoke() 方法中,方便集中進行處理。
進行網絡請求能夠分爲同步請求與異步請求,下面分別對這兩種請求方式進行分析。
Response<PostmanGetBean> response = call.execute();
複製代碼
上面已經知道了 call 爲 ExecutorCallbackCall 類型的 Call 接口,點擊 execute() 方法進去看看:
/*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
@Override public Response<T> execute() throws IOException {
return delegate.execute();
}
複製代碼
這裏的 delegate 實際爲 OkHttpCall,點擊這裏的 execute() 方法進去看看:
/*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 if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
//(1)
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//(2)
return parseResponse(call.execute());
}
複製代碼
/*OkHttpCall*/
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
複製代碼
能夠看到,這裏經過 requestFactory.create(args) 構建一個 Request 對象,而後再經過 OkHttpClient 的 newCall() 方法去建立 call。關於 newCall() 怎麼建立 call,這個在講 Android 主流開源框架(三)OkHttp 源碼解析 已經講過了,這裏就不展開來說了。
咱們繼續看下 create() 方法:
/*RequestFactory*/
okhttp3.Request create(Object[] args) throws IOException {
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args.length;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
headers, contentType, hasBody, isFormEncoded, isMultipart);
List<Object> argumentList = new ArrayList<>(argumentCount);
for (int p = 0; p < argumentCount; p++) {
argumentList.add(args[p]);
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.get()
.tag(Invocation.class, new Invocation(method, argumentList))
.build();
}
複製代碼
能夠看到,這裏的 httpMethod(http 方法)、baseUrl、relativeUrl(相對路勁)、headers(請求頭)等常量就是前面第(3)步中經過解析網絡請求接口註解獲得的,經過這些常量就能夠構建一個 Request 了。
接下來就是解析響應回來的數據了,點擊 parseResponse() 方法進去看看:
/*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();
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) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
// (2.1)
T body = responseConverter.convert(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;
}
}
複製代碼
前面那些狀態碼的檢查等就很少說了,咱們直接看下關注點(2.1),是的,這裏就是經過咱們設置的 Converter 將響應的數據解析成相應的實體類返回的。咱們看下 convert() 方法:
/*GsonResponseBodyConverter*/
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
T result = adapter.read(jsonReader);
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return result;
} finally {
value.close();
}
}
複製代碼
很簡單,就是經過 Gson 將響應數據解析成相應的實體類返回。
call.enqueue(new Callback<PostmanGetBean>() {
@Override
public void onResponse(Call<PostmanGetBean> call, Response<PostmanGetBean> response) {
}
@Override
public void onFailure(Call<PostmanGetBean> call, Throwable t) {
}
});
複製代碼
點擊 enqueue() 方法進去看看:
/*ExecutorCallAdapterFactory-ExecutorCallbackCall*/
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
//(1)
delegate.enqueue(new Callback<T>() {
//(2)
@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);
}
}
});
}
//(3)
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
複製代碼
/*OkHttpCall*/
@Override 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 {
//(1.1)
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//(1.2)
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//(1.3)
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
//(1.4)
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
//(1.5)
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
複製代碼
小結: 這一步的同步與異步請求其實底層分別調用的是 OkHttp 的同步與異步請求方法,而後經過咱們設置的數據轉換器(Converter)將響應的數據解析成相應的實體類返回。同步請求會直接返回,異步請求則會切換到主線程再進行返回。
參考資料: