Retrofit2 源碼解析
1. Retrofit是什么
來自Retrofit官網(wǎng)的介紹:
A type-safe HTTP client for Android and Java
一個基于OkHttp的RESTFUL Api請求工具
2. Retrofit的用法
- 創(chuàng)建Retrofit對象
public static final String API_URL = "https://api.github.com";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
- api新建一個Java接口,用Java注解來描述這個api
public interface GitHubApiService {
@GET("/users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
// you can add some other meathod
}
- 這個retrofit對象創(chuàng)建一個GitHubService對象:
GitHubApiService gitHubApiService=retrofit.create(GitHubApiService.class);//獲取API接口的實(shí)現(xiàn)類的實(shí)例對象
Call<List<Repo>> call = gitHubApiService.listRepos("octocat");
- 異步回調(diào)結(jié)果
// 請求數(shù)據(jù)黎泣,并且處理response
call.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Response<List<Repo>> list) {
Log.d("list: " + list);
}
@Override
public void onFailure(Throwable t) {
}
});
- 同步回調(diào)結(jié)果
List<Repo> list = call.execute();
3. Retrofit的原理
Retrofit整個工作流程:將java接口窖壕,轉(zhuǎn)換成一個個Http請求隶症,交由Okhttp進(jìn)行發(fā)送阀蒂。
Retrofit庫依賴了Okhttp庫和okio庫,核心庫分為Http包和其他核心類驮捍。
Http包里面包含各種定義的注解危虱,幫忙開發(fā)者定義請求方式和請求參數(shù)赫舒。
如何將一個請求放到Okhttp里面去?
答案是: Retrofit利用了java的動態(tài)代理技術(shù)悍及。
動態(tài)代理代碼如下:
/** Create an implementation of the API defined by the {@code service} interface. */
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
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 serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
首先看動態(tài)代理方法,這里是j接口動態(tài)代理的標(biāo)準(zhǔn)寫法
Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler(){});
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 serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
api對象其實(shí)是一個動態(tài)代理對象,類型是接口類型接癌,真正的對象是被動態(tài)代理攔截心赶,然后調(diào)用Proxy.newProxyInstance方法中的InvocationHandler對象。
其中invoke方法:
Object proxy: 代理對象
Method method:調(diào)用的方法
Object... args:方法的參數(shù)
然后Retrofit就會用Java反射獲取到方法的注解信息扔涧,配合args參數(shù)园担,創(chuàng)建一個ServiceMethod對象届谈。
ServiceMethod就像是一個處理器,傳入Retrofit對象和Method對象弯汰,調(diào)用各個接口和解析器艰山,最終生成一個Request,包含api 的域名咏闪、path曙搬、http請求方法、請求頭鸽嫂、是否有body纵装、是否是multipart等等。最后返回一個Call對象据某,Retrofit2中Call接口的默認(rèn)實(shí)現(xiàn)是OkHttpCall橡娄,它默認(rèn)使用OkHttp3作為底層http請求client
翻譯請求方式和參數(shù),同時構(gòu)建出ServiceMethod對象
public ServiceMethod build() {
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);
}
if (httpMethod == null) {
throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
}
if (!hasBody) {
if (isMultipart) {
throw methodError(
"Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
}
if (isFormEncoded) {
throw methodError("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; 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);
}
if (relativeUrl == null && !gotUrl) {
throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
}
if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
throw methodError("Non-body HTTP method cannot contain @Body.");
}
if (isFormEncoded && !gotField) {
throw methodError("Form-encoded method must contain at least one @Field.");
}
if (isMultipart && !gotPart) {
throw methodError("Multipart method must contain at least one @Part.");
}
return new ServiceMethod<>(this);
}
總結(jié)來說:使用Java動態(tài)代理的目的就要攔截被調(diào)用的Java方法癣籽,然后解析這個Java方法的注解挽唉,最后生成Request由OkHttp發(fā)送。