Android源码进阶之深入理解Retrofit工作原理

移动开发 Android
Retrofit是一个基于AOP思想,对RestfulApi注解进行动态代理的网络框架;今天我们就来探讨下实现原理,一起进步。

[[422495]]

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

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声明了返回值是一个Retrofit的Call对象,并且声明了这个对象处理的数据类型为BizEntity,BizEntity是我们自定义的数据模型;

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对象。


责任编辑:武晓燕 来源: Android开发编程
相关推荐

2021-10-15 09:19:17

AndroidSharedPrefe分析源码

2021-09-10 07:31:54

AndroidAppStartup原理

2021-08-24 07:53:28

AndroidActivity生命周期

2021-09-17 06:55:50

AndroidLayoutView

2021-09-30 07:36:51

AndroidViewDraw

2023-09-19 22:47:39

Java内存

2021-09-07 06:40:25

AndroidLiveData原理

2022-09-05 22:22:00

Stream操作对象

2021-09-15 07:31:33

Android窗口管理

2021-09-16 06:44:04

Android进阶流程

2014-07-15 17:17:31

AdapterAndroid

2021-10-26 17:52:52

Android插件化技术

2017-08-08 09:15:41

前端JavaScript页面渲染

2021-09-24 08:10:40

Java 语言 Java 基础

2021-10-10 13:31:14

Java负载均衡算法

2021-10-21 10:02:37

Java开发代码

2021-02-17 11:25:33

前端JavaScriptthis

2016-10-26 20:49:24

ReactJavascript前端

2021-03-10 10:55:51

SpringJava代码

2022-11-04 09:43:05

Java线程
点赞
收藏

51CTO技术栈公众号