一.概述
我想作為一個Android開發(fā)如果沒有聽過Retrofit網(wǎng)絡(luò)請求框架蕉饼,那么他是與時代脫節(jié)的,你隨便在gayhub
上找一個開源的Android項目都能發(fā)現(xiàn)Retrofit的身影碘梢。在介紹Retrofit之前,大家還是需要知道okhttp的基本使用方式的碰镜。
okhttp使用很簡單鲫惶,主要分為同步請求和異步請求:
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
- 同步請求,即等待請求結(jié)果返回岗憋,然后對結(jié)果進(jìn)行解析歌亲,主要流程:
Response response = client.newCall(request).execute();
然后對返回的結(jié)果Request進(jìn)行解析。
- 異步請求澜驮,即回調(diào)式請求陷揪,結(jié)果返回后進(jìn)行回調(diào),不會阻塞當(dāng)前線程的執(zhí)行杂穷。主要流程:
Call call = client.newCall(request);
call.enqueue(new Callback() {
@Override
public void onResponse(Response response) throws IOException {
//get result from response.
}
@Override
public void onFailure(Request arg0, IOException arg1) {
}
});
可以看到悍缠,兩種操作方式,都是對返回的Response進(jìn)行包括io操作耐量、結(jié)果轉(zhuǎn)換等解析操作飞蚓。你可以把okhttp就當(dāng)成類似HttpURLConnection
一樣的Api,他們的功能是一樣的都是負(fù)責(zé)網(wǎng)絡(luò)請求廊蜒。如果想對Okhttp進(jìn)一步了解的可以參考:Android OkHttp完全解析 是時候來了解OkHttp了趴拧。
二.Retrofit的使用
既然都會使用okhttp了,那么使用Retrofit就很容易了山叮。網(wǎng)上教程一大堆著榴,這邊主要羅列基本的3個操作:
- 1.創(chuàng)建接口類,其中每個方法都是一個對應(yīng)的網(wǎng)絡(luò)請求屁倔。
public interface API {
@POST("/load")
Call<Rep> load();
@Post("/test")
Call<Rep> down();
}
- 2.創(chuàng)建Retrofit對象脑又,方式和Okhttp一樣是通過Builder模式進(jìn)行配置生成對象,其中baseUrl表示表示完整的url前半段,和API中的注解合并成最終的url锐借。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://com.seu.test/")
.build();
- 3.創(chuàng)建異步請求
Call<Rep> call = retrofit.create(API.class).load();
call.enquque(new Callback<Rep> {
@Override
public void onResponse(Call<Rep> call, Response<Rep> response) {
//get result from response
}
@Override
public void onFailure(Call<Rep> call, Throwable t) {
}
});
是不是看去和Okhttp的異步請求一模一樣问麸。你猜對了,Retrofit就是對Okhttp的網(wǎng)絡(luò)請求的封裝钞翔。如果你現(xiàn)在還不了解Retrofit的用途严卖,那我還能怎么辦呢。Retrofit2.0使用詳解布轿,我也只能幫到這了哮笆。
三.Retrofit源碼分析
注解Annotation
通過查看源碼可以知道在retrofit2.http目錄下面都是Java注解類俺亮,其中包括熟悉的GET
、POST
等疟呐。本文結(jié)合GET
進(jìn)行入門式介紹脚曾。
Java中包括4種元注解(用于自定義注解的注解):
- Documented,如果定義了該注解启具,則通過javadoc后本讥,該注解的信息可以在docs中顯示(對于開發(fā)來說,沒啥用鲁冯,不行就在自定義注解中直接加上就行)拷沸。
- Inherited 定義該注解是否可以在子類中被繼承。一般該注解的使用也不會很多薯演,至今沒用過繼承的注解撞芍。
- Retention 定義了該注解的存活周期。這個是我見過用處比較大的注解了跨扮,但是在我解釋了他的用處以后序无,你也會覺得
so easy
。Retention包括三部分:
Rentention 的三個enum屬性 |
---|
SOURCE: 說明該自定義注解衡创,只存在與XX.java中帝嗡,在編譯后的XX.class都是找不到的。比如@Override璃氢,可以通過jd-gui來查看XX.class文件哟玷,確實找不到該注解,該注解是在虛擬機層面進(jìn)行解析的一也,實際自定義注解中大概也不會用到該類別巢寡。 |
CLASS : 存在于XX.java和XX.class文件中,但是在運行時Java虛擬機會忽略該注釋椰苟,只有在編譯階段會讀取到該注解抑月,在運行時通過反射是獲取不到該注解的,可以查看Butter Knife 尊剔,他就是編譯時注解爪幻。 |
RUNTIME: 和class屬性一樣,都存在于源文件以及編譯生成的class文件中须误,不同的是在運行時可以得到該注解,即可以運行時可以通過反射得到仇轻。 |
- Target表示該注解可以用在哪個地方京痢,比如:用于屬性字段、用于方法聲明篷店、用于包聲明祭椰、用于接口聲明等等臭家,詳細(xì)的可以參考ElementType。
在了解了Annotation的元注解以后方淤,我們來解釋GET
:
//還不是小兒科钉赁,直接忽略
@Documented
//說明該注解是使用在方法上的,且只能用在方法上
@Target(METHOD)
//說明該注解是運行時注解携茂,可以通過反射得到
@Retention(RUNTIME)
public @interface GET {
//需要有一個屬性
String value() default "";
}
怎么樣你踩,一個自定義的注解就可以完成了,媽媽再也不用擔(dān)心我看不懂注解咯讳苦。其實所有的自定義注解都是照貓畫虎带膜,沒啥困難的。還想深入全名學(xué)習(xí)注解的可以參考:Java注解(Annotation)鸳谜,保證藥到病除膝藕。到現(xiàn)在為止,retrofit2.http下面所有的類基本都可以知道它們的真實含義了吧咐扭。
動態(tài)代理
在將ServiceMethod之前還是需要稍微介紹下Java的動態(tài)代理技術(shù):動態(tài)代理是jdk封裝好的比較好用的類代理生成方式芭挽,Proxy.newProxyInstance有三個參數(shù):
- ClassLoader,這個比較通用蝗肪,直接用類本身的Classloader就可以览绿。
- Class<?>[] interfaces,表示該生成代理類要實現(xiàn)的接口功能穗慕。
- InvocationHandler h饿敲,接口功能的真正實現(xiàn)是h。
Proxy.newProxyInstance是通過反射的方式生成代理類逛绵,通過源碼分析可以看到怀各,Class對象是通過ProxyGenerator
生成的字節(jié)碼,不信的話你可以通過ProxyGenerator
生成的byte[]保存到xx.class文件中术浪,看看是不是把所有的功能都讓h來完成的瓢对,可以參考:JDK動態(tài)代理的實現(xiàn)及原理,保證讓你對動態(tài)代理有比較深刻的理解胰苏。
看到了沒有硕蛹,說話算話,真的很簡單的介紹了動態(tài)代理硕并。
開刀Retrofit.java
Retrofit是通過Builder模式生成的對象法焰,所以直接對Retrofit.Builder類進(jìn)行解析,這里只羅列來了屬性和build()倔毙,屬性設(shè)置方法略(沒啥特別的埃仪,都是一堆類似set方法):
public static final class Builder {
//Retrofit運行的平臺:具體實現(xiàn)類比較簡單,其中包括了Android平臺陕赃、IOS平臺卵蛉、還有java8平臺颁股。這里你就直接理解為Android平臺吧。
private Platform platform;
//該接口直接實現(xiàn)類為OkHttpClient傻丝,相信我甘有。
private okhttp3.Call.Factory callFactory;
private HttpUrl baseUrl;
private List<Converter.Factory> converterFactories = new ArrayList<>();
private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
//網(wǎng)絡(luò)請求返回后是不在主線程的,那么是不是需要有一個主線程的executor來執(zhí)行ui操作葡缰,猜對了就是他亏掀。
private Executor callbackExecutor;
//我暫時理解為懶加載的意思,如果為true則把所有的methond都進(jìn)行反射运准,如果為false則用到哪個method再去反射幌氮。
private boolean validateEagerly;
}
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();
}
// 這里會默認(rèn)添加一個適配器该互,這才能讓API中的load方法返回Call<Rep>
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
//即加入ExecutorCallAdapterFactory
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// 沒有給默認(rèn)解析器,所以要自己定義一個解析器
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
adapterFactories:這個東西舉個栗子吧韭畸,由于adapterFactories中會默認(rèn)添加ExecutorCallAdapterFactory宇智,所以我們看到API中的方法返回Call是默認(rèn)可以解析的。如果接觸過RxJava的同學(xué)應(yīng)該知道胰丁,為了讓API中的方法能夠返回Observable随橘,則需要添加一個自定義Retrofit Adapters,而RxJavaCallAdapterFactory就是能夠讓API返回Observable的具體實現(xiàn)机蔗。所以你想自定義API中函數(shù)的返回類型,就需要添加自定義Adapter甘萧,而常見的Adapter在GayHub上都給有給出萝嘁。
converterFactories:還是很好理解的,一般的網(wǎng)絡(luò)請求后返回的body都是String形式的扬卷,那么怎么把String形式轉(zhuǎn)換成具體的entity對象呢牙言?就是他來實現(xiàn)的。你可以用Gson怪得、jackson來解析返回的數(shù)據(jù)咱枉,只要你配置了真確的converter就可以啦。retrofit-converters徒恋,而默認(rèn)中converterFactories為空的沒有具體的實現(xiàn)蚕断,所以在初始化Retrofit的時候需要添加例如:addConverterFactory(GsonConverterFactory.create())的converter。
這種可插拔式的框架因谎,可以靈活的適配項目之前已經(jīng)使用的一些框架基括。如果retrofit只支持fastjson解析,而之前項目都用Gson解析的财岔,那你會不會把之前的Gson都用fastjson替換(不然有潔癖的我是無法容忍的)风皿?這就是retrofit可插拔的優(yōu)點。
現(xiàn)在創(chuàng)建API的實例,通過Retrofit.create():
public <T> T create(final Class<T> service) {
//確保API是一個接口匠璧,且該接口沒有繼承其他接口
Utils.validateServiceInterface(service);
//看看是不是懶加載桐款,如果true則會把API里面的所有method都進(jìn)行一次性緩存。
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//動態(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);
}
//android平臺返回的是false夷恍,所以不管它
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
//API中每個方法都會生成對應(yīng)的一個ServiceMethod魔眨,解析該方法對應(yīng)的Annotation信息。
ServiceMethod serviceMethod = loadServiceMethod(method);
//他就是okhttp3.Call的一個包裝類酿雪,真正的網(wǎng)絡(luò)請求在這里面進(jìn)行遏暴,且是通過okhttp3.Call進(jìn)行的。
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
//返回該API中method的返回類型指黎,例如Call<Rep>
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
adapt()默認(rèn)返回的是ExecutorCallbackCall朋凉,他就是Call子類,所以你也可以直接把它理解為API函數(shù)的返回類型醋安,例如load的返回值Call<Rep>杂彭,沒有任何問題。
ServiceMethod咋一眼看去不知道什么鬼東西吓揪,其實他的存在就是:把API中的每個method方法解析成一個ServiceMethod對象亲怠,解析的內(nèi)容就是該method對應(yīng)的注解信息,把這些信息存儲在對應(yīng)的ServiceMethod對象中進(jìn)行緩存等待網(wǎng)絡(luò)請求使用柠辞。
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
//緩存团秽,不多說,總不可能在多次調(diào)用API方法的時候叭首,每次都使用反射進(jìn)行解析吧习勤。
serviceMethodCache.put(method, result);
}
}
return result;
}
下面就要開始解析ServiceMethod.Builder()方法,按照慣例只寫屬性和builder方法放棒。這樣姻报,一個method中所有信息都被保存在ServiceMethod當(dāng)中,后續(xù)的網(wǎng)絡(luò)請求只需要獲取ServiceMethod當(dāng)中的數(shù)據(jù)即可间螟。
static final class Builder<T> {
//需要介紹嗎吴旋,就是上面loadServiceMethod傳入的this
final Retrofit retrofit;
//動過動態(tài)代理得到的method,后續(xù)要詳細(xì)的解析該method
final Method method;
//作用于該method的所有注解厢破,比如API方法中的GET
final Annotation[] methodAnnotations;
//method方法中參數(shù)對應(yīng)的注解荣瑟,一個參數(shù)可以對應(yīng)多個注解
final Annotation[][] parameterAnnotationsArray;
//method中參數(shù)對應(yīng)的真實類型列表
final Type[] parameterTypes;
//method返回類型,就是你看到的Call<Rep>摩泪。
Type responseType;
//這一堆boolean自行腦補吧
boolean gotField;
boolean gotPart;
boolean gotBody;
boolean gotPath;
boolean gotQuery;
boolean gotUrl;
String httpMethod;
boolean hasBody;
boolean isFormEncoded;
boolean isMultipart;
String relativeUrl;
Headers headers;
MediaType contentType;
Set<String> relativeUrlParamNames;
ParameterHandler<?>[] parameterHandlers;
Converter<ResponseBody, T> responseConverter;
CallAdapter<?> callAdapter;
public ServiceMethod build() {
//獲取CallAdapter笆焰,可以通過該函數(shù)知道,最終會調(diào)用retrofit.callAdapter(returnType, annotations);
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
//最終調(diào)用retrofit.responseBodyConverter(responseType, annotations);
responseConverter = createResponseConverter();
//猜都不用猜见坑,肯定是解析retrofit2.http下面的注解嚷掠,解析完之后上面的一堆boolean值差不多都賦值好咯捏检。
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
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);
}
//解析對應(yīng)參數(shù)和該參數(shù)的注解列表。
Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
}
return new ServiceMethod<>(this);
}
}
通過對上述對Method的解析不皆,得到了一一對應(yīng)的ServiceMethod對象贯城,最后通過serviceMethod.callAdapter.adapt(okHttpCall)
返回一個API中函數(shù)的返回類型。由于系統(tǒng)默認(rèn)了使用ExecutorCallAdapterFactory
霹娄,可以對比DefaultCallAdapterFactory
能犯,他們的作用是一樣的,只是網(wǎng)絡(luò)請求返回的一個回調(diào)在主線程犬耻,一個回調(diào)在子線程:
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
//這里的returnType是API中函數(shù)的返回類型踩晶,比如Call<Rep>
//responseType則是Rep,就是returnType對應(yīng)的泛型類型枕磁。
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
//看看渡蜻,直接把call返回了,這就是為什么可以處理API中函數(shù)是Call返回類型的原因透典。
return call;
}
};
}
}
好了晴楔,到這里Call<Rep> call = retrofit.create(API.class).load()
我們就得到了Call,接下去就是enqueue了峭咒,這個過程有沒有很像Okhttp的enqueue税弃,知識點有沒有!前面我們已經(jīng)知道了這里對應(yīng)的Call就是OkhttpCall(不清楚的直接在動態(tài)代理里面找)凑队,所以要enqueue就去找OkhttpCall:
@Override public void enqueue(final Callback<T> callback) {
//這是啥東西则果,哈哈,就是要去真真網(wǎng)絡(luò)請求的Call漩氨,不是retrofit的call西壮。
//如果還是混淆了這兩個call,那么只能再多看看Okhttp是怎么網(wǎng)絡(luò)請求的叫惊。
okhttp3.Call call;
Throwable failure;
//禁止同一個call入列兩次
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//從該函數(shù)的命名就可以看到他的意思款青,創(chuàng)建真實的Call,即Okhttp的Call
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//真真的網(wǎng)絡(luò)請求來了霍狰,這一塊網(wǎng)絡(luò)請求就是Okhttp真真的網(wǎng)絡(luò)請求抡草,看到這你要還不懂的話,怪我咯蔗坯。
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
//通過真實返回的rawResponse康震,解析得到retrofit的response
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
//把成功的結(jié)果傳遞給回調(diào),其中response中包好了解析好的對象宾濒。
private void callSuccess(Response<T> response) {
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
//把okhttp的返回rawResponse轉(zhuǎn)換成retrofit的Response腿短,并把解析內(nèi)容放入到Response當(dāng)中。
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
//該rawResponse只解析返回內(nèi)容的頭部,可以了解NoContentResponseBody讀取body拋異常了
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) {
return Response.success(null, rawResponse);
}
//異常捕獲response橘忱?不了解具體的用處赴魁。。鹦付。
ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
try {
//這個地方厲害了尚粘,就是通過convert來轉(zhuǎn)換成具體的對象择卦,比如用GsonConverterFactory.create()來把String轉(zhuǎn)換成具體對象敲长。自行看代碼,其實就一句話秉继。
T body = serviceMethod.toResponse(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
catchingBody.throwIfCaught();
throw e;
}
}
到這里祈噪,我們就把解析好的T body
返回給回調(diào)接口Callback里面去了,現(xiàn)在我們就可以在onResponse里面通過Response.body()獲取到解析以后的真正對象咯尚辑。
好了基本上能說的都說了辑鲤,應(yīng)該能夠差不多了解了Retrofit的使用流程了。
Utils
Retrofit中有一個非常重要的工具類Utils杠茬,里面包含了所有的關(guān)于Type
的反射方法月褥,比如通過Call<Rep>得到Rep的Type等等。
Type
是和泛型相關(guān)的接口瓢喉,具體分為4種類型:
- ParameterizedType:具體的泛型類型宁赤,比如ArrayList<String>中,具體泛型就是String栓票。
- TypeVariable:泛型變量决左,在泛型類里面使用該泛型變量,這時候該變量就是TypeVariable走贪。
- GenericArrayType:泛型數(shù)組佛猛,String[],懂坠狡?
- WildcardType:通配符泛型继找,比如ArrayList<? extends Number>。
不想講了逃沿,自己參考:Java中的Type詳解
四.總結(jié)
本文通過Okhttp的網(wǎng)絡(luò)請求為主線婴渡,然后把Retrofit是如何包裝Okhttp的過程進(jìn)行了分析,到這里你應(yīng)該有能力去修改Retrofit去適應(yīng)你自己app里面的業(yè)務(wù)功能了吧感挥。
最后再把參考的幾篇文章進(jìn)行羅列下缩搅,歡迎提出問題,我說雞蛋你說要: