聲明:原創(chuàng)作品春霍,轉載請注明出處http://www.reibang.com/p/2186d666b1ee
做Android開發(fā)的小伙伴應該對Retrofit這個庫非常熟悉,Retrofit是Android中最常用的一個網(wǎng)絡請求庫贴唇,如果你還沒用過這個庫可以上Retrofit的官網(wǎng)或者可以看我之前寫的一篇關于Retrofit使用介紹:點擊查看。今天主要總結分析下Retrofit的源碼實現(xiàn)钓账,當然現(xiàn)在網(wǎng)上也有大量的關于Retrofit源碼分析的文章垒手,之所以再寫一遍主要還是作為自己的總結以加深印象胡嘿。
1.使用簡介
在開始源碼分析之前我們還是簡單看下Retrofit的使用:
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
首先我們創(chuàng)建一個Retrofit實例疏魏,這里用github提供的接口測試停做。
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
上面我們定義了一個接口里面有一個查詢GitHub上某個庫的貢獻者名單。
// 創(chuàng)建上面Github接口的實例
GitHub github = retrofit.create(GitHub.class);
// 創(chuàng)建一個Call用來查詢square用戶下retrofit庫的貢獻者名單
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// 執(zhí)行call的請求返回貢獻者名單
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
輸出結果:
=========
JakeWharton (1061)
swankjesse (280)
pforhan (48)
eburke (36)
NightlyNexus (29)
dnkoutso (26)
edenman (24)
loganj (17)
Noel-96 (16)
rcdickerson (14)
rjrjr (13)
adriancole (9)
holmes (8)
Jawnnypoo (8)
JayNewstrom (7)
kryali (7)
swanson (7)
crazybob (6)
danrice-square (5)
vanniktech (5)
Turbo87 (5)
naturalwarren (5)
guptasourabh04 (4)
artem-zinnatullin (3)
chriscizek (3)
codebutler (3)
icastell (3)
jjNford (3)
ojh102 (3)
f2prateek (3)
可以看到我們通過Retrofit的create方法將上面的GitHub接口實例化了大莫,然后調用這個實例化后GitHub的contributors方法并傳入相關的參數(shù)得到一個Call對象蛉腌,接著調用這個Call的execute方法來執(zhí)行具體網(wǎng)絡請求,然后返回請求結構即貢獻者名單只厘。
以上就是Retrofit的簡單使用烙丛,接下來我們就深入源碼來看下它內部是怎么實現(xiàn)的。
2.源碼解析
首先看下Retrofit的初始化:
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
這段代碼很好理解羔味,就是通過builder模式來創(chuàng)建Retrofit對象河咽。另外上面還添加了一個ConverterFactory,這個是用來解析請求的結果介评,這里用Gson來解析。
接著我們主要看下下面這句代碼:
GitHub github = retrofit.create(GitHub.class);
通過調用上面創(chuàng)建的retrofit對象的create方法可以把我們定義的GitHub接口實例化,你可能會比較好奇這個方法是如何做到這點们陆,我們就進入這個create方法看下:
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// 如果調用的方法是在Object類中定義的寒瓦,則直接執(zhí)行該方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
里面代碼你可能看的比較暈,這里其實用到Java里面的動態(tài)代理模式
,如果你還不了解什么是動態(tài)代理建議你可以先參考這篇文章坪仇,知道了什么是動態(tài)代理杂腰,上面的代碼還是比較好理解的。通過Java自帶的APIProxy.newProxyInstance
方法可以動態(tài)創(chuàng)建出我們上面定義的Github接口的實例椅文,這個方法需要傳入接口的類加載器喂很、Class對象以及一個InvocationHandler對象,當我們調用接口中定義的方法皆刺,比如contributors這個方法少辣,根據(jù)動態(tài)代理性質,最終會調用到InvocationHandler對象的invoke方法羡蛾,這個方法會回調三個參數(shù):創(chuàng)建的代理對象漓帅、調用的方法對象Method、以及調用這個方法時傳入的參數(shù)數(shù)組痴怨。接著我們看invoke里面的代碼:如果調用的方法是Object中定義的方法忙干,則直接執(zhí)行該方法并返回。否則如果這個方法是平臺默認方法則會執(zhí)行該默認方法并返回浪藻,一般我們這里定義的都不是默認方法所以會執(zhí)行這句代碼loadServiceMethod(method).invoke(args)
捐迫,這句代碼通過loadServiceMethod方法返回一個ServiceMethod實例,然后調用這個對象的invoke方法爱葵,所以接下來我們來看下loadServiceMethod
方法內部實現(xiàn):
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方法把傳入的Method對象解析成ServiceMethod對象,然后將得到的ServiceMethod方法存入緩存并返回浓瞪。所以接下來就有必要來看下parseAnnotations是如何將Method轉為ServiceMethod的:
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
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.");
}
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
可以看到parseAnnotations方法是一個靜態(tài)方法懈玻,并且ServiceMethod是一個抽象類,那么這個方法最后返回的一定是ServiceMethod的一個實現(xiàn)類對象乾颁,我們來看下里面的具體實現(xiàn)涂乌,里面邏輯也是很清晰,首先通過RequestFactory的parseAnnotations方法來創(chuàng)建一個RequestFactory對象英岭,然后根據(jù)這個RequestFactory對象和其他一些參數(shù)通過HttpServiceMethod類的parseAnnotations方法來創(chuàng)建一個ServiceMethod的實現(xiàn)類對象湾盒。我們分別來看下這幾個方法的實現(xiàn),首先來看下RequestFactory的parseAnnotations方法:
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
首先parseAnnotations是一個靜態(tài)方法诅妹,里面通過Builder來創(chuàng)建具體的RequestFactory實例罚勾,我們分別看下Builder的構造方法和build方法:
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
這里沒什么特別的東西毅人,就是把傳入的retrofit對象和method對象賦值,然后得到method的方法注解尖殃、參數(shù)類型以及參數(shù)注解數(shù)組
注:由于Retrofit中主要是通過注解來配置丈莺,所以再接著分析代碼之前,你還需要對Java注解有一定的了解送丰。
接下來看下build方法:
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
if (httpMethod == null) {
throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
method,
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError(
method,
"FormUrlEncoded can only be specified on HTTP methods with "
+ "request body (e.g., @POST).");
}
}
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
if (relativeUrl == null && !gotUrl) {
throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError(method, "Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError(method, "Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError(method, "Multipart method must contain at least one @Part.");
}
return new RequestFactory(this);
}
這個方法里面看上去代碼很多缔俄,其實主要做的事情很簡單,就是解析方法注解和方法中參數(shù)的注解器躏,解析完成之后就創(chuàng)建RequestFactory對象返回俐载,其他都是一些判空或者錯誤處理。所以接下來分別看下是怎么進行方法注解和參數(shù)注解解析的登失。首先進行方法注解解析的代碼如下:
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
上面代碼遍歷方法上的所有注解遏佣,然后調用parseMethodAnnotation方法進行挨個解析,我們看下parseMethodAnnotation方法:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} 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();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
可以看到上面代碼是根據(jù)不同的注解執(zhí)行不同的解析規(guī)則壁畸,這里就拿我我上面例子中Github接口中定義的contributors方法舉例贼急,這個方法上面的注解是@GET
,所以我們就只看@GET
注解的解析:
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
if (this.httpMethod != null) {
throw methodError(
method,
"Only one HTTP method is allowed. Found: %s and %s.",
this.httpMethod,
httpMethod);
}
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
// Get the relative URL path and existing query string, if present.
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);
}
我們知道一個get請求,url后面可以用?
然后后面帶參數(shù)請求捏萍,這個Retrofit中鏈接后面不能這樣直接帶參數(shù)太抓,上面方法會有個判斷如果帶了參數(shù)就會拋出一個異常提示讓你用@Query
注解來傳get的參數(shù)。除此之外就是屬性的賦值令杈,比如把@GET
注解后的路徑賦值給relativeUrl走敌,然后如果@GET
注解后路徑有可變參數(shù),就會調用parsePathParameters方法來解析path逗噩。parsePathParameters具體里面代碼實現(xiàn)就不展開來了掉丽,就是用一個正則表達式來解析。
上面是關于方法注解的解析异雁,接下來看下方法參數(shù)注解是怎么解析的:
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] = parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
上面代碼通過parseParameter方法會把每個參數(shù)解析成一個ParameterHandler對象捶障,后續(xù)會用到這個對象,我們看下具體的解析方法:
private @Nullable ParameterHandler<?> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler<?> result = null;
if (annotations != null) {
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction =
parseParameterAnnotation(p, parameterType, annotations, annotation);
if (annotationAction == null) {
continue;
}
if (result != null) {
throw parameterError(
method, p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
}
if (result == null) {
if (allowContinuation) {
try {
if (Utils.getRawType(parameterType) == Continuation.class) {
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
這個里面通過調用parseParameterAnnotation方法把注解解析成ParameterHandler對象纲刀,如果一個參數(shù)有多個注解就會拋出異常项炼。接著看下parseParameterAnnotation方法,這個方法其實就是針對不同的注解進行不同的處理示绊,由于方法比較長锭部,下面就拿@PATH
舉例子:
validateResolvableType(p, type);
if (gotQuery) {
throw parameterError(method, p, "A @Path parameter must not come after a @Query.");
}
if (gotQueryName) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryName.");
}
if (gotQueryMap) {
throw parameterError(method, p, "A @Path parameter must not come after a @QueryMap.");
}
if (gotUrl) {
throw parameterError(method, p, "@Path parameters may not be used with @Url.");
}
if (relativeUrl == null) {
throw parameterError(
method, p, "@Path can only be used with relative url on @%s", httpMethod);
}
gotPath = true;
Path path = (Path) annotation;
String name = path.value();
validatePathName(p, name);
Converter<?, String> converter = retrofit.stringConverter(type, annotations);
return new ParameterHandler.Path<>(method, p, name, converter, path.encoded());
可以看到前面是一些異常的處理,后面就是解析@PATH
注解中的值面褐,然后封裝成一個ParameterHandler對象返回拌禾。這樣我們的參數(shù)注解也解析完了。然后我們回到上面的build
方法中展哭,參數(shù)注解解析完后就會創(chuàng)建一個RequestFactory對象返回湃窍,其實這個對象里就是對我們向服務器請求的數(shù)據(jù)做一層封裝闻蛀。到這build
方法執(zhí)行完了,其實ServiceMethod的parseAnnotations方法中的第一句代碼執(zhí)行完了,即:
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
接著我們看下面這句關鍵的代碼:
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
這句代碼調用HttpServiceMethod的parseAnnotations方法并根據(jù)上面我們創(chuàng)建的RequestFactory對象返回一個ServiceMethod對象您市,我們進入這個方法看下:
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
// Unwrap the actual body type from Response<T>.
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
// TODO figure out if type is nullable or not
// Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
// Find the entry for method
// Determine if return type is nullable or not
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
throw methodError(
method,
"'"
+ getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// TODO support Unit for Kotlin?
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
上面代碼比較長循榆,我們挑重點分析下,首先會根據(jù)adapterType來生成一個CallAdapter對象墨坚,這個CallAdapter用到了適配器模式
,如果你還不是很熟悉適配器模式可以參看這篇文章:Java 大白話講解設計模式之 -- 適配器模式映挂,在Retrofit中這個CallAdapter的作用就是把Retrofit中的Call轉為你想要的一個請求對象泽篮,比如你想要Retrofit與RxJava結合使用,那么需要把這個Call轉為Observable柑船,這個轉換工作就是由CallAdapter來做的帽撑,當然根據(jù)你要轉為不同的對象,這個CallAdapter也是不一樣的鞍时,如果是RxJava的話就需要一個RxJava的CallAdapter亏拉,另外CallAdapter是由CallAdapterFactory來創(chuàng)建的,不同的CallAdapter他的CallAdapterFactory自然也不一樣逆巍,所以在Retrofit中CallAdapterFactory由開發(fā)者自己傳入及塘,比如你要使用RxJava那在Retrofit的初始化配置中就要傳入RxJava2CallAdapterFactory,如下代碼:
addCallAdapterFactory(RxJava2CallAdapterFactory.create())
有了CallAdapter我們再接著往下看:
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
如果不是kotlin協(xié)程(暫時先不談論kotlin協(xié)程這種情況锐极,因為我對協(xié)程也不是很熟練2333333)則直接創(chuàng)建一個CallAdapted
對象返回,我們看下這個CallAdapted對象:
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
}
可以看到這個CallAdapted是繼承自HttpServiceMethod類笙僚,而HttpServiceMethod類又是繼承自ServiceMethod,我們再一開始分析動態(tài)代理的時候知道調用網(wǎng)絡請求接口的方法時最后都會轉換到執(zhí)行ServiceMethod的invoke方法
:
loadServiceMethod(method).invoke(args);
所以我們看下ServiceMethod的invoke方法:
abstract @Nullable T invoke(Object[] args);
在ServiceMethod中這個invoke是個抽象方法灵再,所以我們到他的子類HttpServiceMethod中看下:
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
可以看到子類有具體實現(xiàn)肋层,方法中創(chuàng)建了一個OkHttpCall,其實看名字就可以看得出來這個OkHttpCall通過我們傳入的參數(shù)里面封裝了OkHttp的東西翎迁,由于篇幅有限就不展開看了栋猖,然后會調用adapt方法,在HttpServiceMethod類中這是一個抽象方法汪榔,所以在其子類實現(xiàn)也就是剛提到的CallAdapted類,在上面CallAdapted中adapt方法是調用了callAdapter對象的adapt方法蒲拉,也就是上面?zhèn)魅氲倪m配器對象,它會把這個原生的OkHttpCall轉為所需要的對象揍异。上面我們說過要轉成什么對象需要自己傳入對應的CallAdapter的工廠類全陨,但是上面例子中我們在初始化Retrofit時也沒有傳入這個工廠對象,其實如果不傳的話Retrofit會使用里面默認的CallAdapterFactory對象衷掷,
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
可以看到這個默認的工廠類會創(chuàng)建一個默認的CallAdapter辱姨,而這個CallAdapter的adapt其實就是直接返回這個傳入的call對象,也就是上面的OkHttpCall戚嗅,當然這里還有個executor是否為空的判斷雨涛,默認是為空枢舶,這里就不深入討論。
3.與RxJava結合使用
我們知道Retrofit是可以和RxJava結合使用的替久,之所以能結合我們上面也提到了凉泄,是通過CallAdapter把OkHttpCall轉為Observable被觀察者對象。代碼上只需要在Retrofit初始化時傳入RxJavaCallAdapterFactory對象:
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(API_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
接下來把接口方法返回的Call對象改為Observale:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Observable<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
當執(zhí)行contributors方法后就會返回一個被觀察者Observable對象蚯根,有了這個就可以根據(jù)RxJava規(guī)則進行鏈式調用后众。
如果你之前沒看過Retrofit源碼你可能會感到比較好奇這是怎么做到和RxJava完美結合,現(xiàn)在我們看完源碼再來看下它是怎么實現(xiàn)颅拦,其實關鍵還是上面我們提到的CallAdapter蒂誉,我們就來看下RxJava的CallAdapter是怎么實現(xiàn)的,我們進入RxJavaCallAdapterFactory距帅,然后最后我們會找到一個RxJavaCallAdapter:
final class RxJavaCallAdapter<R> implements CallAdapter<R, Object> {
private final Type responseType;
private final @Nullable Scheduler scheduler;
private final boolean isAsync;
private final boolean isResult;
private final boolean isBody;
private final boolean isSingle;
private final boolean isCompletable;
RxJavaCallAdapter(
Type responseType,
@Nullable Scheduler scheduler,
boolean isAsync,
boolean isResult,
boolean isBody,
boolean isSingle,
boolean isCompletable) {
this.responseType = responseType;
this.scheduler = scheduler;
this.isAsync = isAsync;
this.isResult = isResult;
this.isBody = isBody;
this.isSingle = isSingle;
this.isCompletable = isCompletable;
}
@Override
public Type responseType() {
return responseType;
}
@Override
public Object adapt(Call<R> call) {
OnSubscribe<Response<R>> callFunc =
isAsync ? new CallEnqueueOnSubscribe<>(call) : new CallExecuteOnSubscribe<>(call);
OnSubscribe<?> func;
if (isResult) {
func = new ResultOnSubscribe<>(callFunc);
} else if (isBody) {
func = new BodyOnSubscribe<>(callFunc);
} else {
func = callFunc;
}
Observable<?> observable = Observable.create(func);
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isSingle) {
return observable.toSingle();
}
if (isCompletable) {
return observable.toCompletable();
}
return observable;
}
}
可以看到這個RxJavaCallAdapter也是實現(xiàn)了Retrofit中的CallAdapter接口右锨,所以我們主要看下它的adapt方法是怎么實現(xiàn)的,可以看到這個方法其實就對OkHttpCall做了層層封裝碌秸,最后封裝成Observable被觀察者對象返回绍移,當Observable發(fā)生訂閱時就會調用里面的OkHttpCall對象的具體請求操作,然后把請求結果回調給觀察者讥电。這樣Retrofit就和RxJava完美結合起來了蹂窖。