秒懂Retrofit2之原始碼詳解
前言
Android開發發展到現在,網路請求的主流已經毫無疑問的是Retrofit
加OkHttp
了,兩年前就使用了Retrofit,但是一直沒有仔細研究其原始碼,前段時間系統梳理了Java註解的知識集結成 秒懂 Java註解型別(@Annotation)一文後,就想看一下優秀框架如何使用註解的,剛好Retrofit大量使用了註解,所以花了3天時間仔細研究了一下。
Retrofit由於原始碼較少,非常適合初次閱讀框架原始碼的同學,但是需要具備Http及Java註解和反射的知識。
概述
Retrofit
是一個封裝了Okhttp
網路請求庫的優秀框架,其可以輕鬆提供Restful
風格的介面,這是它的官方地址
我們要理解一個框架必須要先從熟練使用它開始,一般優秀的框架或者類庫的介面都設計的非常完善,我們首先要熟練的使用這些介面,順著作者暴露的介面去深入才會事半功倍。所以本文準備從簡單使用開始深入原始碼。
Retrofit 用法
1:定義請求接
public interface ApiService{
@GET(value = "users")
Call<User>getUser(@Query("id") int userId);
...
}
2:構建retrofit
例項
下面是最簡單的構建方式,通過鏈式呼叫我們可以為retrofit例項設定很多屬性,例如callAdapter,Conventor,OkhttpClient等。
Retrofit retrofit=new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
3:建立介面的動態代理物件,以呼叫接口裡的方法。
ApiService service=retrofit.create(ApiService.class);
4:發起網路請求 ,處理回撥結果
new ApiHandler().getService().getUser(1)
.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
}
@Override
public void onFailure(Call<User> call, Throwable t) {
}
});
值得注意的是,要想上面的程式碼獲得我們自己設定型別User
,需要我們設定自己的Converter
,預設的只支援返回responseBody
與Void
型別。使用.addConverterFactory(GsonConverterFactory.create())
添加了一個Gson的轉換器。
retrofit完整的使用也就分為上面4步,那如果你已經用的很溜了,曾幾何時是否想過這一切都是怎麼做到的呢?這就是下面要解決的問題。
Retrofit原始碼詳細分析
本文使用的是retrofit
2.4.0 版本,在讀原始碼的時候發現了一個問題,就是從GitHub
拷貝的原始碼,與直接通過gradle
引入Android
專案然後在AndroidStudio
的類庫檢視的程式碼有出入,AndroidStudio
中看少了兩個類,這兩個類被合併了,如下圖所示:
關鍵介面
Retrofit 中有如下4個關鍵介面:
Call : 可以理解為一個完整的Http請求活動,向伺服器發起請求(Request)得到返回結果(Response)。
CallAdapter: 將型別引數為R的Call (Call<R>
)適配為型別T.例如我可以將OkHttpCall<R>
轉換成一個Observable<String>
輸出,Rxjava2Calladapter就是這麼幹的。
Converter:這個一說就明白,將一種型別的資料轉成另一種型別。例如將ResponseBody
轉成User
型別,這個接口裡面的Factory
有三個轉換器:responseBodyConverter
將http請求返回的資料ResponseBody
轉換為我們需要的型別;requestBodyConverter
將我們傳入的型別轉換為http請求時需要的RequestBody,例如我們使用註解@Body
標記了某個型別User
,我們就可以使用這個轉換器來將User構建為RequestBody
的內容。stringConverter
將我們傳入的型別轉換為String
Callback:http
請求回撥,一個成功,一個失敗,這個沒有什麼好說的。
關鍵類:
Retrofit:配置必要引數,發起構建http Call 的動作。
ServiceMethod ,HttpServiceMethod :負責組裝出一個可以使用的Http 請求的方法,就是根據註解以及我們設定的返回型別,使用我們在retrofit
配置的calladapter
以及converter
來構建請求方法。HttpServiceMethod
依賴RequestFactory
。
RequestFactory , RequestBuilder : RequestFactory
的主要目的是構建一個okhttp3.Request
,這就看出了要懂retrofit
必須先要對OkHttp
有所瞭解的必要性了。而 RequestBuilder
是協助RequestFactory
來完成這個目標的,它從RequestFactory
裡面得到一些必要資料後構建出了okhttp3.Request
。RequestFactory
依賴RequestBuilder
。
ParameterHandler:用來處理我們宣告的方法裡引數上的那些註解的,例如@path
,@Url
等。其協助RequestFactory
工作,RequestFactory
依賴ParameterHandler
。
OkHttpCall:終於找到了你,這個類才是真正發起http
請求的地方,上面的所有類都是為它服務。
Retrofit構建
Retrofit 類:
public Retrofit build() {
...
//設定okhttp callFactory
okhttp3.Call.Factory callFactory = this.callFactory;
...
//可以設定Executor,一般我們都不設定,直接使用系統預設的,對於Android來說就是一個使用Handler切換到主執行緒的executor
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//設定CallAdapter.Factory列表
//我們可以通過addCallAdapterFactory()方法加入我們自己的adapter,例如rxjava2的callAdapter,
//系統會在其後加入一個預設的Adapter,一旦使用者沒有指定介面卡就使用預設的,Android使用ExecutorCallAdapterFactory
List<CallAdapter.Factory> callAdapterFactories =
new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
//設定Converter.Factory列表
//與上面不同的是,這次首先加入一個預設的,然後才加入使用者設定的,上面是先新增使用者的,然後新增預設的。
List<Converter.Factory> converterFactories =
new ArrayList<>(1 + this.converterFactories.size());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
//使用設定好的引數來構建Retrofit例項
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
通過build()
方法我構建Retrofit
例項,接下來就可以呼叫它的public
方法了,其中最為關鍵的就是create()
方法. Retrofit
通過此方法根據我們的介面宣告來構建一個Call
,然後我們就可以發起網路請求相關操作了。
獲得介面代理
Retrofit 類:
public <T> T create(final Class<T> service) {
//省略了檢查我們介面檔案的合法性以及是否要將所有請求方法快取起來的程式碼
...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();//獲得當前平臺,此處為Android
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
...
//最關鍵的就是這句話,獲取一個ServiceMethod物件然後構建一個Call物件。
return loadServiceMethod(method).invoke(args);
}
});
}
此方法採用了Jdk
動態代理模式,不清楚動態代理的同學請移步到秒懂Java代理與動態代理模式。其實也比較好理解,你定義了一個介面,裡面聲明瞭很多方法,那你正常情況下是不是應該有一個這個介面的實現類,然後才能使用裡面的方法。所謂動態代理就代理的這個實體類,我們這裡的create()
返回值就是此動態代理例項,只要使用這個例項呼叫方法,那麼都會進入invoke()
方法裡面。
Retrofit 類:
//獲取一個ServiceMethod,其代表一個完整的http請求方法,先從快取中拿,如果沒有就使用ServiceMethod.parseAnnotations去產生,然後放入快取中,再返回
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.parseAnnotations(this, method);
進入ServiceMethod類檢視
ServiceMethod類
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//檢查我們什麼方法返回型別是否合法
...
return new HttpServiceMethod.Builder<Object, T>(retrofit, method).build();
}
abstract T invoke(@Nullable Object[] args);
}
可以看到,真正的邏輯實現是在HttpServiceMethod
中,包括構建以及起調。
構建ServiceMethod
HttpServiceMethod類的任務很簡單,就是想辦法獲得下面這幾個欄位的值
HttpServiceMethod類
private final RequestFactory requestFactory;
private final okhttp3.Call.Factory callFactory;
private final CallAdapter<ResponseT, ReturnT> callAdapter;
private final Converter<ResponseBody, ResponseT> responseConverter;
然後通過invoke()
調起方法
//提交一個http請求,返回我們設定的型別的結果,例如Call<User>
@Override ReturnT invoke(@Nullable Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
雖然目的很明確,但是實施過程還是要費一番周折的,下面我們就來抽絲剝繭。
callFactory 的值從傳入retrofit例項裡面很容易得到的,我們接下來分析剩下的三個
HttpServiceMethod類
callFactory = builder.retrofit.callFactory();//得到了callFactory 的值
HttpServiceMethod<ResponseT, ReturnT> build() {
requestFactory = RequestFactory.parseAnnotations(retrofit, method);//得到了requestFactory 的值
callAdapter = createCallAdapter();//得到了callAdapter 的值
responseType = callAdapter.responseType();//介面宣告的返回型別例如Call<User>,那麼這個就User
...
responseConverter = createResponseConverter();//得到了responseConverter 的值
...
return new HttpServiceMethod<>(this);
}
構建RequestFactory
獲取requestFactory 的值:通過RequestFactory類中的parseAnnotations()
方法
RequestFactory類
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
而所謂的構建requestFactory類就是獲得該類中如下欄位的值
RequestFactory類
private final HttpUrl baseUrl;
final String httpMethod;
private final String relativeUrl;
private final Headers headers;
private final MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
private final ParameterHandler<?>[] parameterHandlers;
這個類又使用了構建者模式,關鍵程式碼都在RequestFactory.Builder中。
RequestFactory類
RequestFactory build() {
//解析方法上的註解,例如@GET,@POST等
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);//關鍵程式碼
}
...
//解析引數註解,如@Path,@Query等
//將處理方法引數註解的ParameterHandler放在一個數組中parameterHandlers
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0; p < parameterCount; p++) {
//引數的型別 @QueryMap Map<String,String>map,那麼這個值就是Map<String,String>
Type parameterType = parameterTypes[p];
...
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];//每一個引數上的註解,可能多於一個,所以是陣列
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);//關鍵程式碼
}
...
return new RequestFactory(this);
}
構建requestFactory需要完成兩個主要任務,解析方法上的註解標籤,解析方法引數上的註解標籤。
RequestFactory類
private void parseMethodAnnotation(Annotation annotation) {
...
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
isFormEncoded = true;
}
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
...
this.httpMethod = httpMethod;
this.hasBody = hasBody;
//沒有設定相對路徑就解析完畢了
if (value.isEmpty()) {
return;
}
//解析相對路徑與查詢字串
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
// Ensure the query string does not have any named parameters.
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
if (queryParamMatcher.find()) {
throw methodError(method, "URL query string \"%s\" must not have replace block. "
+ "For dynamic query parameters use @Query.", queryParams);
}
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
通過上面的程式碼就將將方法上面的註解標籤解析好了,接下來看下引數標籤解析,比方法註解複雜一些。下面這個方法是處理方法中某一個引數的邏輯,總體邏輯比較簡單:遍歷此引數上的所有註解,檢視是否存在一個retrofit
註解,可以有其他註解,存在則生成一個當前註解的ParameterHandler
。
RequestFactory類
private ParameterHandler<?> parseParameter(
int p, Type parameterType, Annotation[] annotations) {
ParameterHandler<?> result = null;
//迴圈某一個引數上的註解,因為一個引數除了使用Retrofit的註解標註以外,也有可能使用其他註解標註,這裡只處理retrofit自己的註解
//如果一個引數上存在兩個以上的retrofit註解,則會報錯
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction = parseParameterAnnotation(
p, parameterType, annotations, annotation);//關鍵程式碼
//發現是非Retrofit註解,直接跳過
if (annotationAction == null) {
continue;
}
//發現一個引數上存在兩個以上的Retrofit註解,報錯
if (result != null) {
throw parameterError(method, p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
//某個引數不帶retrofit註解也是不行的,報錯
if (result == null) {
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
生成ParameterHandler的方法長的令人髮指,大概有270多行,但是邏輯比較簡單。就是看下當前要處理的註解是retrofit的那個註解,調到相應的邏輯處理單元去處理,所以如果retrofit再增加新的註解,這個方法還要增長,只要增加一個新的註解那麼這裡就會增加一段處理邏輯。我挑選典型邏輯展示如下
RequestFactory類
private ParameterHandler<?> parseParameterAnnotation(int p, Type type, Annotation[] annotations, Annotation annotation) {
if (annotation instanceof Url) {
...
gotUrl = true;
if (type == HttpUrl.class
|| type == String.class
|| type == URI.class
|| (type instanceof Class && "android.net.Uri".equals(((Class<?>) type).getName()))) {
return new ParameterHandler.RelativeUrl();//關鍵程式碼
} else {
throw parameterError(method, p,
"@Url must be okhttp3.HttpUrl, String, java.net.URI, or android.net.Uri type.");
}
} else if (annotation instanceof Path) {
...
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
//注意這裡就開始使用converter了,在解析@Path註解的引數時,其型別是要被轉換成String的
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(name, converter, path.encoded());
} else if (annotation instanceof Query) {
Query query = (Query) annotation;
String name = query.value();
boolean encoded = query.encoded();
Class<?> rawParameterType = Utils.getRawType(type);
gotQuery = true;
//針對引數型別為Iterable(例如集合),陣列以及其他三種情況作了處理,這段程式碼基本上把ParameterHandler的功能都涉及到了。
if (Iterable.class.isAssignableFrom(rawParameterType)) {
//必須是泛型
if (!(type instanceof ParameterizedType)) {
throw parameterError(method, p, rawParameterType.getSimpleName()
+ " must include generic type (e.g., "
+ rawParameterType.getSimpleName()
+ "<String>)");
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type iterableType = Utils.getParameterUpperBound(0, parameterizedType);
Converter<?, String> converter =
retrofit.stringConverter(iterableType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).iterable();
} else if (rawParameterType.isArray()) {
Class<?> arrayComponentType = boxIfPrimitive(rawParameterType.getComponentType());
Converter<?, String> converter =
retrofit.stringConverter(arrayComponentType, annotations);
return new ParameterHandler.Query<>(name, converter, encoded).array();
} else {
Converter<?, String> converter =
retrofit.stringConverter(type, annotations);
return new ParameterHandler.Query<>(name, converter, encoded);
}
...
}
}
至此我們就將方法的註解以及方法引數的註解都處理完了,然後下一步就是使用這些處理後的資訊來構建okhttp Request 了,只有有了這哥們我們才能發起Http請求。這個任務是由create()
方法完成的
RequestFactory類
//建立了一個okhttp 的 Request,裡面使用到了RequestBuilder 類,這個類負責使用RequestFactory類處理出來的資訊構建http請求
okhttp3.Request create(@Nullable Object[] args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
//下面的程式碼比較關鍵,通過引數資訊來完善RequestBuilder ,而這些引數資訊存放在parameterHandlers裡面,它是通過parseParameter()函式解析出來的
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
...
//關鍵程式碼 使用上面得到的ParameterHandler來配置 RequestBuilder
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
至此,HttpServiceMethod 中的requestFactory 完成了賦值,是不是已經迷失了,請回到我們最初的道路上來,現在HttpServiceMethod 中還有兩欄位callAdapter與responseConverter 需要賦值。
獲取CallAdapter
HttpServiceMethod類
private CallAdapter<ResponseT, ReturnT> createCallAdapter() {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
//關鍵程式碼
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
}
從上面的方法可以看出,這個callAdapter是通過retrofit的例項方法callAdapter()
獲得的,轉到Retrofit類裡面檢視
Retrofit類
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
...
//是否要跳過列表中某個callAdapter
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
...
}
上面的程式碼是從我們那個callAdapter列表獲得一個callAdapter,如果我們不在構建retrofit的時候設定自定義的callAdapter,那麼我們的列表中就只有一個ExecutorCallAdapterFactory
,所以我們得到的callAdapter就是它。
獲取ResponseConverter
HttpServiceMethod類
private Converter<ResponseBody, ResponseT> createResponseConverter() {
Annotation[] annotations = method.getAnnotations();
return retrofit.responseBodyConverter(responseType, annotations);//關鍵程式碼
}
從上面的方法可以看出,這個Converter
是通過retrofit
的例項方法responseBodyConverter()
獲得的,轉到Retrofit類裡面檢視
Retrofit類
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
...
int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
return (Converter<ResponseBody, T>) converter;
}
}
...
}
與獲取callAdapter的路子完全一樣,現在我們那個converter列表裡面也只有一個值BuiltInConverters
,所以獲取到的就是它。
至此終於構建出了HttpServiceMethod例項,我們可以愉快的呼叫它的例項函式啦。
呼叫函式
還記得我們的出發點嗎?就是retrofit的create()裡的loadServiceMethod(method).invoke(args);
,我們前面分析了那麼多就解釋了loadServiceMethod(method)這半句話,那麼下面就是invoke(args)。
HttpServiceMethod類
//提交一個http請求,返回我們設定的型別的結果,例如Call<User>
@Override ReturnT invoke(@Nullable Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
那個callAdapter就是通過ExecutorCallAdapterFactory得到的,來看其原始碼
ExecutorCallAdapterFactory類
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) {
//返回的型別必須是Call 型別
if (getRawType(returnType) != Call.class) {
return null;
}
//例如returnType為:List<User> 那麼responseType 就是User
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);
}
};
}
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) {
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);
}
});
}
});
}
...
}
上面的原始碼有三點需要注意
1:看那個get()
,就是這裡返回了我起調函式的那個callAdapter,所以adapt(),執行的就是
@Override public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
可以看到其需要一個Call<Object>
型別的引數,我們傳入的是OkHttpCall
的例項,其實現了Call介面。
2:這裡面還有一個CallbackExecutor,這個是在retrofit構建時候傳入的,Android平臺預設為MainThreadExecutor
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
handler.post(r);
}
}
不知道是不是有親切的感覺,尼瑪總看到了關於Android的東西,Handler!這個CallbackExecutor負責切換執行緒。所以網路請求後得到的回撥就在主執行緒中了。
3:這裡使用了典型的代理模式,不過這次是靜態代理。ExecutorCallbackCall 代理了OkHttpCall,他們都實現了介面Call。這樣代理就有能力在執行被代理物件的動作是附加一些動作了,例如這裡的執行緒切換。妙哉否?妙哉!
發起請求
至此我們已經成功得到了Call的例項物件OkHttpCall,可呼叫開始呼叫其相關方法發起網路請求了。
OkHttpCall 這個類和okhttp就耦合的比較嚴重了,它就是為okhttp而生的。下面我們就分析其中最為關鍵的部分,也是我們最為常用的非同步請求:
OkHttpCall 類
@Override public void enqueue(final Callback<T> callback) {
...
okhttp3.Call call;
Throwable failure;
synchronized (this) {
//這就是為什麼我們不能使用同一個例項發起多次請求的原因,需要clone
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) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//真正發起非同步請求的地方,這個call是okhttp的call
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);//關鍵程式碼
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
...
});
}
程式碼比較簡單了,建立okhttp3.call,呼叫其enqueue 方法發起非同步請求,處理回撥結果。我們主要看關鍵部分
1:建立okhttp3.call使用的是call = rawCall = createRawCall();
我們看一下createRawCall()
的原始碼
private okhttp3.Call createRawCall() throws IOException {
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
...
return call;
}
我們知道要建立一個okhtt3.call,需要使用okhttp的Call.Factory.newCall(Request r)
的方法,那個callFactory就是我們的工廠,而Request是由RequestFactory.create()
的方法建立的,我們必須看一下這個方法:
RequestFactory 類
//建立了一個okhttp 的 Request,裡面使用到了RequestBuilder 類,這個類負責使用RequestFactory類處理出來的資訊構建http請求
okhttp3.Request create(@Nullable Object[] args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
//下面的程式碼比較關鍵,通過引數資訊來完善RequestBuilder ,而這些引數資訊存放在parameterHandlers裡面,
//它是通過parseParameter()函式解析出來的
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
這個方法可以說是至關重要,我們前面搞來搞去就是為了能生成一個okhttp3的一個Request。
2:第二個就是如何處理返回的結果 response = parseResponse(rawResponse);
OkHttpCall類
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//省略處理網路錯誤結果的程式碼
...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
T body = responseConverter.convert(catchingBody);//關鍵程式碼
return Response.success(body, rawResponse);
}
將返回的okhttp3
的response
解析為retrofit
的response
。關鍵是是通過responseConverter
將返回資料解析成我們在介面宣告檔案中提供的資料型別,就是那個callAdapter
裡面的responseType()
,其是由returnType
得到的,而 returnType = method.getGenericReturnType();
,就是我們宣告時候的泛型型別。
思考
我們為什麼要閱讀一個優秀框架的原始碼呢,首先閱讀其原始碼有助於我們更加熟練的使用此框架,但是這不應該成為我們的主要目的。我們的主要目的應該是學習其優秀的設計思想,以及其某些程式碼處理手段,以便於我們日後也可以寫出這麼優秀的東西來。
此次閱讀retrofit
原始碼,有幾點印象深刻:
1:我對其callAdapter
,以及Conveter
上的設計感到由衷的佩服,通過這樣的設計大大增強了可擴充套件性,就是因為這樣優秀的設計才使得我們可以整合rxjava2
等優秀的框架到retrofit
上。:
2:大量使用了構造者模式,簡化了複雜的物件生成過程。
3:其動態代理以及靜態代理使用的也是恰到好處,對我既有相關知識是一個很好的增強。
4:處理註解的手法,特別是處理方法引數註解時的手法特別值得我們認真學習。
總結
這是我初次完整分析一個框架原始碼,以前都是區域性檢視,這就註定了很難全盤把握作者的設計思想。這種事情自己覺得有時間可以長做,最好可以參與到其中。