前言
Retrofit是個極其優(yōu)秀的庫,特別是和rxjava結合起來爽航,使用起來那是一個絲滑般爽审姓。不過使用了一兩年廷雅,現在才好好想起總結總結奏纪,下面直接用實際項目中的部分代碼來進行分析Retrofit幫我們做了什么壳鹤。
簡單的使用
- 創(chuàng)建Retrofit
Retrofit.Builder().baseUrl(BuildConfig.HOST)
.client(OKHTTP_CLIENT_NO_TOKEN)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
- 創(chuàng)建含有網絡請求信息的ApiService
- 獲取ApiService
fun getApiService(): ApiService = RETROFIT.create(ApiService::class.java)
- 調用ApiService 接口方法
Builder 過程
Retrofit 通過 Builder 模式創(chuàng)建:
先來直接看 Builder.build方法:
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);
}
這是標準的build函數盛龄,檢測一些可配置參數,沒有設置就是賦默認值,然后創(chuàng)建目標對象余舶。
創(chuàng)建Retrofit對象6個參數
這里創(chuàng)建Retrofit需要6個參數:
1. callFactory:okhttp3.Call.Factory
這里是我們傳入的OkHttpClient 對象啊鸭,如果我們沒有傳入,build方法會為我們創(chuàng)建一個默認的OkhttpClient對象匿值。
2. baseUrl:HttpUrl
我們傳入host字符串后會經過HttpUrl.parse(baseUrl);
處理后轉換為HttpUrl對象赠制。
3. converterFactories:List<Converter.Factory>
-
Converter.Factory 是什么?挟憔。
CallAdapter.Factory是個抽象類:
里面有三個方法:- responseBodyConverter()返回Converter<ResponseBody, ?>對象(負責把ResponseBody對象轉為其他對象)
- requestBodyConverter()返回Converter<?, RequestBody>對象(負責把其他對象轉為RequestBody)
- stringConverter()返回Converter<?,String>對象(把其他對象轉為String對象)
其中Converter<F,T>對象是個轉換接口钟些,里面convert方法,負責把F轉換為T對象绊谭。
-
converterFactories 里面有哪些對象政恍?
- BuiltInConverters 對象
在builder的構造方法里面創(chuàng)建了BuiltInConverters對象并添加到集合里面
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()); }
- GsonConverterFactory 對象
在上面配置中我們添加了GsonConverterFactory.create() 實際是GsonConverterFactory 對象
- BuiltInConverters 對象
4. adapterFactories:List<CallAdapter.Factory>
CallAdapter.Factory 是什么?
一個抽象類达传,里面也是3個方法
a. get():CallAdapter 返回一個CallAdapter 適配器對象(有兩個方法篙耗,一返回Type,二把Call轉為其他對象的adapter方法)
b. getParameterUpperBound() :Type 返回參數上限類型
c. getRawType 獲取原始類型adapterFactories里面對象是什么宪赶?
a. RxJava2CallAdapterFactory 對象
在前面配置中.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
實際是RxJava2CallAdapterFactory對象
b. ExecutorCallAdapterFactory 對象
在build方法中會執(zhí)行adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
宗弯,里面實際創(chuàng)建的是ExecutorCallAdapterFactory對象。
5. callbackExecutor:Executor
回調執(zhí)行者逊朽。
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
而platform變量在builder構造方法中被初始化罕伯,Android平臺下是Android對象,而platform.defaultCallbackExecutor();獲得的是MainThreadExecutor對象
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
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);
}
}
}
}
6. validateEagerly:boolean
默認為false
create 方法做了什么
在剛開始用Retrofit時候一直很好奇叽讳,我們創(chuàng)建ApiService接口追他,沒有創(chuàng)建任何實現對象,經過Create方法就可以直接調用接口的方法岛蚤。
它這個方法里面做了什么呢邑狸。
來看下create的代碼:
public <T> T create(final Class<T> service) {
//驗證是否是接口
Utils.validateServiceInterface(service);
//前面?zhèn)魅氲膮担瑸閒alse
if (validateEagerly) {
// 如果為true涤妒,會執(zhí)行eagerlyValidateMethods方法单雾,
//里面會遍歷接口的方法,創(chuàng)建對應的ServiceMethod對象并加到換成中
eagerlyValidateMethods(service);
}
// 關鍵點K稀9瓒选!創(chuàng)建動態(tài)代理對象
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<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
這個方法主要分3部分贿讹,第一部分驗證傳入的class是否是接口渐逃,第二部分根據builder中參數是否預先創(chuàng)建ServiceMethod對象,而第三部分是最為重要的民褂,創(chuàng)建動態(tài)代理對象茄菊。
動態(tài)代理Proxy
理解動態(tài)代理的同學疯潭,這部分可以跳過。面殖。竖哩。。
簡單的介紹下動態(tài)代理脊僚,利用反射技術在運行時候創(chuàng)建給定接口的動態(tài)代理對象實例相叁。而創(chuàng)建這種代理對象也很簡單,
Proxy.newProxyInstance()
里面接受3個參數:
- ClassLoader 類加載器
- Class<?>[] interfaces 接口class 數組
- InvocationHandler 接口吃挑。
invoke 方法有3個參數:
- proxy:Object 動態(tài)代理對象钝荡,可用來獲取類信息,方法舶衬,annotation等
- method:Method 被代用的方法埠通,可獲取方法名,參數逛犹,返回類型等信息
- args:Object[] 方法參數
而動態(tài)代理類的方法都會交給實現了InvocationHandler接口對象的invoke()處理端辱。所以我們使用Retrofit創(chuàng)建了ApiService 代理對象。然后調用里面的方法時候其實是調用invoke方法
Retrofit中create方法內InvocationHandler對象的invoke方法
上面我們已經搞起動態(tài)代理是什么東西虽画,而Retrofit之所以能幫我們實現接口定義的方法就是因為這里面的invoke舞蔽。
@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<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
前面兩個判斷是保證后面的執(zhí)行的我們定義的方法。后面才是重點码撰,
這里也分了3個步驟:
- 根據method 加載得到ServiceMethod
- 創(chuàng)建 OkHttpCall 對象渗柿。
- 調用 serviceMethod.callAdapter.adapt(okHttpCall)
ServiceMethod 是什么?
ServiceMethod 其實是個適配器脖岛,把我們接口定義的方法轉為 http call朵栖。
里面除了一個Builder類還有3個重要的方法:
- toRequest():Request 生成請求
- toResponse():Response 生成響應
- parsePathParameters():Set<String> 解析路徑參數
先來看下serviceMethod怎么得到的,在invoke中調用了loadServiceMethod方法
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;
}
上面可看到柴梆,如果緩存中有就直接使用陨溅,沒有就通過new ServiceMethod.Builder<>(this, method).build();
創(chuàng)建。
ServiceMethod 的創(chuàng)建
先來分析下ServiceMethod的創(chuàng)建過程绍在,上文知道先創(chuàng)建Builder對象然后調用build方法门扇。
- Builder構造
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
這個構造方法做的事情比較簡單,就五個成員變量賦值偿渡,分別是Retrofit對象臼寄,方法對象,方法注解溜宽,參數類型脯厨,參數注解(二維數組)
- build 方法
這個方法比較長,我們省略一些異常步驟:
public ServiceMethod build() {
// 獲取callAdapter
callAdapter = createCallAdapter();
// 獲取相應類型
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?");
}
// 賦值響應轉換器
responseConverter = createResponseConverter();
// 解析方法注解
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
...
確保請求方式坑质,請求體等信息正確
...
// 方法參數數量
int parameterCount = parameterAnnotationsArray.length;
// 參數交給 ParameterHandler處理
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);
}
...
確保請求參數信息正確
...
// 創(chuàng)建 ServiceMethod 對象
return new ServiceMethod<>(this);
}
來大概過一下這個build方法合武,先獲取callAdapter->再通過Retrofit獲取Converter<Response,?>轉換器->解析方法注解獲取請求方式->解析方法參數獲取請求參數->構成ServiceMethod對象。
2.1 callAdapter 是什么對象涡扼?
createCallAdapter方法內最終通過(CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
獲取callAdapter對象稼跳,最終調用Retrofit的nextCallAdapter方法:
public CallAdapter<?, ?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
//skipPast = null 所以是-1+1 = 0
int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
這里會返回adapterFactories集合中第一個適配器工廠類返回的適配器,RxJava2CallAdapterFactory(如果沒有添加rxjava那就是默認的工廠類 ExecutorCallAdapterFactory 類)吃沪,查看RxJava2CallAdapterFactory對象的get方法汤善,發(fā)現返回的是RxJava2CallAdapter對象。
所以callAdapter是RxJava2CallAdapter對象(如果沒添加rxjava票彪,使用默認红淡,這個對象是CallAdapter匿名子類)。
2.2 responseConverter 是什么對象降铸?
同樣ServiceMethod的 createResponseConverter 方法最終會調用 Retrofit的responseBodyConverter方法
retrofit.responseBodyConverter(responseType, annotations);
最終調用nextResponseBodyConverter方法:
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(Converter.Factory skipPast,
Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");
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) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
....
....
}
這里的邏輯和nextCallAdapter類似在旱,不過converterFactories中的第一個對象是BuiltInConverters,它的responseBodyConverter方法當方法返回類型不是ResponseBody或Void時候返回空的Converter對象推掸。
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == ResponseBody.class) {
return Utils.isAnnotationPresent(annotations, Streaming.class)
? StreamingResponseBodyConverter.INSTANCE
: BufferingResponseBodyConverter.INSTANCE;
}
if (type == Void.class) {
return VoidResponseBodyConverter.INSTANCE;
}
return null;
}
所以我們看下GsonConverterFactory :
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
所以我們等下用GsonResponseBodyConverter分析桶蝎。
2.3 解析方法 注解
主要是parseMethodAnnotation方法:
private void parseMethodAnnotation(Annotation annotation) {
....
else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
....
}
里面對不同類型注解進行處理,最終都是交給parseHttpMethodAndPath方法谅畅,我們以GET為例子登渣。
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
...
...
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
其實里面是獲取注解內的信息然后存儲在成員變量中
2.4 解析方法參數注解
主要是生成 parameterHandler,parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
而parseParameter方法最終調用parseParameterAnnotation方法生成 ParameterHandler
而ParameterHandler是個抽象類毡泻,不同注解有不同實現
OkHttpCall 對象
構造器里面接受ServiceMethod 與 方法參數
RxJava2CallAdapter 的 adapter 方法
最后create方法返回的是serviceMethod.callAdapter.adapt(okHttpCall)
對象
@Override public Object adapt(Call<R> call) {
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return observable;
}
這里返回不同的Rx 被觀察者對象 胜茧,我們以CallExecuteObservable為例看看里面怎么執(zhí)行的
CallExecuteObservable 訂閱方法
熟悉Rxjava的同學都知道,當Observable被訂閱時候仇味,會執(zhí)行Observable的subscribeActual方法呻顽,我先來看下CallExecuteObservable的subscribeActual方法:
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
// okhttpCall 對象
Call<T> call = originalCall.clone();
observer.onSubscribe(new CallDisposable(call));
boolean terminated = false;
try {
// 重要步驟,發(fā)送網絡請求獲取響應
Response<T> response = call.execute();
if (!call.isCanceled()) {
observer.onNext(response);
}
if (!call.isCanceled()) {
terminated = true;
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!call.isCanceled()) {
try {
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
可以看到里面有個重要的步驟Response<T> response = call.execute();
通過OkhttpCall的execute方法獲取響應然后進行后面Rx的onNext等步驟邪铲。
OkhttpCall 的execute 方法
來看下實際執(zhí)行網絡請求:
@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 {
throw (RuntimeException) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
call = rawCall = createRawCall();
} catch (IOException | RuntimeException e) {
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
return parseResponse(call.execute());
}
可以看到里面其實分3個工作:
- 創(chuàng)建Okhttp3.Call
- 執(zhí)行execute獲取響應
- 執(zhí)行parseResponse 轉換響應
而其中execute獲取響應是調用Okhttp中的方法芬位,所以我們來分析下其他兩個步驟:
- createRawCall 得到okhttp.Call
private okhttp3.Call createRawCall() throws IOException {
// 獲取請求
Request request = serviceMethod.toRequest(args);
// 其實是OkhttpClient.newCall()方法
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
終于看見關鍵步驟了,這方法里先通過serviceMethod 獲取請求带到,而service.callFactory是調用Retrofit中的callFactory昧碉,也就是我們build中傳入的OkhttpClient對象,所以其實Retrofit還是通過Okhttp進行網絡請求揽惹。
- 執(zhí)行parseResponse
前面已經獲取了原始的響應了被饿,怎么獲取
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.
// 讓響應source可傳遞
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();
}
}
// 204,205 響應成功,無數據
if (code == 204 || code == 205) {
rawBody.close();
return Response.success(null, rawResponse);
}
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
// 獲取響應搪搏,把響應體的包裝類通過serviceMethod處理
T body = serviceMethod.toResponse(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;
}
}
上面分析可以看到原始的響應體經過serviceMethod.toResponse(catchingBody);
處理變成我們想要的數據狭握。然后把得到的數據包裝成Response(并非ok中的Response)對象。
而ServiceMethod中的toResponse也很簡單:
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
而responseConverter對象在之前分析了是GsonResponseBodyConverter,所以我們看下它的convert方法:
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
這就是可以得到們要想對bean類的原因疯溺。
總結
至此论颅,我們Retrofit的流程分析的差不多了“タ眩現在來總結下流程:
- 通過Retrofit.Builder 生成Retrofit對象(主要傳入,baseUrl恃疯,OkhttpClient漏设,coverter,callAdapter等)今妄。
- 調用create方法得到接口的代理對象郑口。
- 執(zhí)行接口的某個方法觸發(fā)代理對象的invoke方法。
- 通過Builder模式盾鳞,生成 ServiceMethod 對象(期間犬性,進行變量賦值,解析方法中注解path腾仅,解析請求參數等信息)
- 生成OkhttpCall 對象
- 執(zhí)行callAdapter 中的 adapter 方法(后續(xù)以RxJava2CallAdapter為例)
- 生成XXXObservable等被觀察者對象(后續(xù)以CallExecuteObservable為例)
- 下游訂閱執(zhí)行 subscribeActual 方法
- 執(zhí)行OkhttpCall方法生成響應(期間使用serviceMethod生成Okhttp的Request乒裆,經過Okhttpclien得到call對象,然后生成原始Response攒砖,然后使用serviceMethod中的converter轉換原始響應得到Gson處理后的響應數據)
- 完成一次請求缸兔。