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