新聞中心
?[[422495]]??
本文轉(zhuǎn)載自微信公眾號(hào)「Android開(kāi)發(fā)編程」,作者Android開(kāi)發(fā)編程。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開(kāi)發(fā)編程公眾號(hào)。

前言
Retrofit是一個(gè)基于AOP思想,對(duì)RestfulApi注解進(jìn)行動(dòng)態(tài)代理的網(wǎng)絡(luò)框架;
今天我們就來(lái)探討下實(shí)現(xiàn)原理,一起進(jìn)步
一、使用Retrofit
1、包引用
在gradle文件中引用retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:retrofit-converters:2.3.0' compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'
如果需要使用更多擴(kuò)展功能,比如gson轉(zhuǎn)換,rxjava適配等,可以視自己需要繼續(xù)添加引用;
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
如果現(xiàn)有的擴(kuò)展包不能滿(mǎn)足需要,還可以自己擴(kuò)展converter,adapter等;
2、定義接口
Retrofit要求定義一個(gè)網(wǎng)絡(luò)請(qǐng)求的接口,接口函數(shù)里要定義url路徑、請(qǐng)求參數(shù)、返回類(lèi)型;
public interface INetApiService { @GET("/demobiz/api.php") Call getBizInfo(@Query("id") String id); }
在這個(gè)接口定義中,用注解@GET("/demobiz/api.php")聲明了url路徑,用注解@Query("id") 聲明了請(qǐng)求參數(shù);
最重要的是,用Call聲明了返回值是一個(gè)Retrofit的Call對(duì)象,并且聲明了這個(gè)對(duì)象處理的數(shù)據(jù)類(lèi)型為BizEntity,BizEntity是我們自定義的數(shù)據(jù)模型;
3、依次獲得Retrofit對(duì)象、接口實(shí)例對(duì)象、網(wǎng)絡(luò)工作對(duì)象
首先,需要新建一個(gè)retrofit對(duì)象;
然后,根據(jù)上一步的接口,實(shí)現(xiàn)一個(gè)retrofit加工過(guò)的接口對(duì)象;
最后,調(diào)用接口函數(shù),得到一個(gè)可以執(zhí)行網(wǎng)絡(luò)訪(fǎng)問(wèn)的網(wǎng)絡(luò)工作對(duì)象;
//新建一個(gè)Retrofit對(duì)象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN)//要訪(fǎng)問(wèn)的網(wǎng)絡(luò)地址域名,如http://www.zhihu.com .addConverterFactory(GsonConverterFactory.create()) .build(); ... //用retrofit加工出對(duì)應(yīng)的接口實(shí)例對(duì)象 INetApiService netApiService= retrofit.create(INetApiService.class); //可以繼續(xù)加工出其他接口實(shí)例對(duì)象 IOtherService otherService= retrofit.create(IOtherService.class); ··· //調(diào)用接口函數(shù),獲得網(wǎng)絡(luò)工作對(duì)象 CallcallWorker= netApiService.getBizInfo("id001");
這個(gè)復(fù)雜的過(guò)程下來(lái),最終得到的callWorker對(duì)象,才可以執(zhí)行網(wǎng)絡(luò)訪(fǎng)問(wèn)。
4、訪(fǎng)問(wèn)網(wǎng)絡(luò)數(shù)據(jù)
用上一步獲取的worker對(duì)象,執(zhí)行網(wǎng)絡(luò)請(qǐng)求
callWorker.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) {...} @Override public void onFailure(Call call, Throwable t) {...} });
在回調(diào)函數(shù)里,取得我們需要的BizEntity數(shù)據(jù)對(duì)象
二、Retrofit實(shí)現(xiàn)原理
???
1、Retrofit對(duì)象的構(gòu)建就是簡(jiǎn)單的builder模式,直接看create
//Retrofit.java publicT create(final Class service) { //驗(yàn)證 validateServiceInterface(service); return (T) //動(dòng)態(tài)代理 Proxy.newProxyInstance( service.getClassLoader(), //類(lèi)加載器 new Class>[] {service}, //一組接口 new InvocationHandler() { //判斷android和jvm平臺(tái)及其版本 private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object[] args){ //如果該方法是Object的方法,直接執(zhí)行不用管 if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } //isDefaultMethod:檢查是否是java8開(kāi)始支持的接口默認(rèn)方法 return platform.isDefaultMethod(method) ? platform.invokeDefaultMethod(method, service, proxy, args) : loadServiceMethod(method).invoke(args); //我們關(guān)注這里 } }); }
Proxy.newProxyInstance動(dòng)態(tài)代理,運(yùn)行期會(huì)生成一個(gè)類(lèi)(字節(jié)碼)如$ProxyN,實(shí)現(xiàn)傳入的接口即WanApi,重寫(xiě)接口方法然后轉(zhuǎn)發(fā)給InvocationHandler的invoke
class $ProxyN extends Proxy implements WanApi{ Call articleList(@Path("page") int page){ //轉(zhuǎn)發(fā)給invocationHandler invocationHandler.invoke(this,method,args); } }
2、validateServiceInterface驗(yàn)證邏輯
//Retrofit.java private void validateServiceInterface(Class> service) { //檢查:WanApi不是接口就拋異常... //檢查:WanApi不能有泛型參數(shù),不能實(shí)現(xiàn)其他接口... if (validateEagerly) { //是否進(jìn)行嚴(yán)格檢查,默認(rèn)關(guān)閉 Platform platform = Platform.get(); for (Method method : service.getDeclaredMethods()) { //遍歷WanApi方法 //不是默認(rèn)方法,并且不是靜態(tài)方法 if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { //把方法提前加載進(jìn)來(lái)(檢查下有沒(méi)有問(wèn)題) loadServiceMethod(method); } } } }
如果開(kāi)了validateEagerly,會(huì)一次性把接口WanApi的所有方法都檢查一遍并加載進(jìn)來(lái),可以在debug模式下開(kāi)啟,提前發(fā)現(xiàn)錯(cuò)誤寫(xiě)法,比如在@GET請(qǐng)求設(shè)置了@Body這種錯(cuò)誤就會(huì)拋出異常:
java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.
3、loadServiceMethod
然后是loadServiceMethod(method).invoke(args),看名字可知是先找方法,然后執(zhí)行
//Retrofit.java //緩存,用了線(xiàn)程安全ConcurrentHashMap final Map> serviceMethodCache = new ConcurrentHashMap<>(); ServiceMethod> loadServiceMethod(Method method) { ServiceMethod> result = serviceMethodCache.get(method); //WanApi的articleList方法已緩存,直接返回 if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { //解析articleList的注解,創(chuàng)建ServiceMethod并緩存起來(lái) result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); } } return result; }
跟進(jìn)ServiceMethod.parseAnnotations
//ServiceMethod.java staticServiceMethod parseAnnotations(Retrofit retrofit, Method method) { //1. RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); //檢查:articleList方法返回類(lèi)型不能用通配符和void... //2. return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); }
4、 RequestFactory.parseAnnotations
//RequestFactory.java static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { return new Builder(retrofit, method).build(); } class Builder { RequestFactory build() { //解析方法注解如GET for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //省略各種檢查... //解析參數(shù)注解如Path 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); } //省略各種檢查... return new RequestFactory(this); } }
得到RequestFactory后, HttpServiceMethod.parseAnnotations,HttpServiceMethod負(fù)責(zé)適配和轉(zhuǎn)換處理,將接口方法的調(diào)用調(diào)整為HTTP調(diào)用
//HttpServiceMethod.java //ResponseT響應(yīng)類(lèi)型如WanArticleBean,ReturnT返回類(lèi)型如Call staticHttpServiceMethod parseAnnotations( Retrofit retrofit, Method method, RequestFactory requestFactory) { //省略kotlin協(xié)程邏輯... Annotation[] annotations = method.getAnnotations(); //遍歷找到合適的適配器 CallAdapter callAdapter = createCallAdapter(retrofit, method, adapterType, annotations); //得到響應(yīng)類(lèi)型,如WanArticleBean Type responseType = callAdapter.responseType(); //遍歷找到合適的轉(zhuǎn)換器 Converter responseConverter = createResponseConverter(retrofit, method, responseType); okhttp3.Call.Factory callFactory = retrofit.callFactory; return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); }
5、最終返回了一個(gè)CallAdapted,看到CallAdapted
//CallAdapted extends HttpServiceMethod extends ServiceMethod class CallAdaptedextends HttpServiceMethod { private final CallAdapter callAdapter; CallAdapted( RequestFactory requestFactory, okhttp3.Call.Factory callFactory, Converter responseConverter, CallAdapter callAdapter) { super(requestFactory, callFactory, responseConverter); this.callAdapter = callAdapter; } @Override protected ReturnT adapt(Call call, Object[] args) { //適配器 return callAdapter.adapt(call); } }
回到Retrofit.Builder
//Retrofit.Builder.java public Retrofit build() { Executor callbackExecutor = this.callbackExecutor; //如果沒(méi)設(shè)置線(xiàn)程池,則給android平臺(tái)設(shè)置一個(gè)默認(rèn)的MainThreadExecutor(用Handler將回調(diào)切回主線(xiàn)程) if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } List callAdapterFactories = new ArrayList<>(this.callAdapterFactories); //添加默認(rèn)的DefaultCallAdapterFactory callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); }
DefaultCallAdapterFactory這個(gè)工廠(chǎng)創(chuàng)建具體的CallAdapter實(shí)例
//DefaultCallAdapterFactory.java public CallAdapter, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType); //如果指定了SkipCallbackExecutor注解,就表示不需要切回主線(xiàn)程 final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : callbackExecutor; return new CallAdapter6、invoke
前邊loadServiceMethod得到了CallAdapted,然后執(zhí)行invoke,實(shí)現(xiàn)在父類(lèi)HttpServiceMethod里,
//HttpServiceMethod.java final ReturnT invoke(Object[] args) { //終于見(jiàn)到okhttp了! Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); return adapt(call, args); } class CallAdapted extends HttpServiceMethod { private final CallAdapter callAdapter; @Override protected ReturnT adapt(Call call, Object[] args) { //用前邊得到的適配器,把OkHttpCall包成ExecutorCallbackCall return callAdapter.adapt(call); } }
然后是請(qǐng)求入隊(duì),ExecutorCallbackCall.enqueue -> OkHttpCall.enqueue,
//ExecutorCallbackCall.java void enqueue(final Callbackcallback) { delegate.enqueue( new Callback () { @Override public void onResponse(Call call, final Response response) { //將回調(diào)切回主線(xiàn)程 callbackExecutor.execute( () -> { callback.onResponse(ExecutorCallbackCall.this, response); }); //... } @Override public void onFailure(Call call, final Throwable t) {} }); } //OkHttpCall.java void enqueue(final Callback callback) { //okhttp邏輯 okhttp3.Call call; call.enqueue(new okhttp3.Callback() { void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { callback.onResponse(OkHttpCall.this, response); } }) }
到此整個(gè)流程就通了
二、功能擴(kuò)展
1、OkHttpClient
Retrofit使用OkHttpClient來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求,這個(gè)OkHttpClient雖然不能替換為其他的網(wǎng)絡(luò)執(zhí)行框架比如Volley,但是Retrofit允許我們使用自己擴(kuò)展OkHttpClient,一般最常擴(kuò)展的就是Interceptor攔截器了;
OkHttpClient mClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { try { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Accept-Charset", "UTF-8"); builder.addHeader("Accept", " application/json"); builder.addHeader("Content-type", "application/json"); Request request = builder.build(); return chain.proceed(request); } catch (Exception e) { e.printStackTrace(); } return null; } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .client(mClient) .build();
2、addConverterFactory
擴(kuò)展的是對(duì)返回的數(shù)據(jù)類(lèi)型的自動(dòng)轉(zhuǎn)換,把一種數(shù)據(jù)對(duì)象轉(zhuǎn)換為另一種數(shù)據(jù)對(duì)象;
GsonConverterFactory可以把Http訪(fǎng)問(wèn)得到的json字符串轉(zhuǎn)換為Java數(shù)據(jù)對(duì)象BizEntity,這個(gè)BizEntity是在INetApiService接口中要求的的;
如果現(xiàn)有的擴(kuò)展包不能滿(mǎn)足需要,可以繼承Retrofit的接口。retrofit2.Converter
在創(chuàng)建Retrofit對(duì)象時(shí),可以插入我們自定義的ConverterFactory;
//retrofit對(duì)象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定義Converter .build();
3、addCallAdapterFactory
擴(kuò)展的是對(duì)網(wǎng)絡(luò)工作對(duì)象callWorker的自動(dòng)轉(zhuǎn)換,把Retrofit中執(zhí)行網(wǎng)絡(luò)請(qǐng)求的Call對(duì)象,轉(zhuǎn)換為接口中定義的Call對(duì)象;
這個(gè)轉(zhuǎn)換不太好理解,我們可以對(duì)照下圖來(lái)理解:
???
Retrofit本身用一個(gè)OkHttpCall的類(lèi)負(fù)責(zé)處理網(wǎng)絡(luò)請(qǐng)求,而我們?cè)诮涌谥卸x需要定義很多種Call,接口里的Call和Retrofit里的OkHttpCall并不一致,所以我們需要用一個(gè)CallAdapter去做一個(gè)適配轉(zhuǎn)換;
這其實(shí)是Retrofit非常核心,也非常好用的一個(gè)設(shè)計(jì),如果我們?cè)诮涌谥幸蟮暮瘮?shù)返回值是個(gè)RxJava的Flowable對(duì)象
public interface INetApiService { @GET("/demobiz/api.php") Flowable getBizInfo(@Query("id") String id); }
那么我們只需要為Retrofit添加對(duì)應(yīng)的擴(kuò)展;
//retrofit對(duì)象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
就能得到Flowable類(lèi)型的callWorker對(duì)象;
//用retrofit加工出對(duì)應(yīng)的接口實(shí)例對(duì)象 INetApiService netApiService= retrofit.create(INetApiService.class); ··· //調(diào)用接口函數(shù),獲得網(wǎng)絡(luò)工作對(duì)象 FlowablecallWorker= netApiService.getBizInfo("id001"); 在這里,callAdapter做的事情就是把retrofit2.Call對(duì)象適配轉(zhuǎn)換為Flowable 對(duì)象;
同樣,如果現(xiàn)有的擴(kuò)展包不能滿(mǎn)足需要,可以繼承Retrofit的接口;retrofit2.CallAdapter
4、動(dòng)態(tài)替換url
在構(gòu)建Retrofit時(shí)傳入HttpUrl對(duì)象,之后這個(gè)實(shí)例就一直存在不會(huì)更改,所以可以反射修改他的字段比如host,來(lái)實(shí)現(xiàn)動(dòng)態(tài)替換服務(wù)端地址;
String SERVER = "https://www.xxx.com/"; HttpUrl httpUrl = HttpUrl.get(SERVER); Retrofit retrofit = new Retrofit.Builder() //.baseUrl(SERVER) .baseUrl(httpUrl) //使用HttpUrl .build();
總結(jié):
1.Retrofit將Http請(qǐng)求抽象成java接口
2.接口里用注解 描述和配置網(wǎng)絡(luò)請(qǐng)求參數(shù)
3.動(dòng)態(tài)代理的方式來(lái)生成call對(duì)象。
文章題目:Android源碼進(jìn)階之深入理解Retrofit工作原理
網(wǎng)站路徑:http://m.5511xx.com/article/dhdjpcs.html


咨詢(xún)
建站咨詢(xún)
