服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android源码进阶之深入理解Retrofit工作原理

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

2021-09-08 22:45Android开发编程 Android

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

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

前言

Retrofit是一个基于AOP思想,对RestfulApi注解进行动态代理的网络框架;

今天我们就来探讨下实现原理,一起进步

一、使用Retrofit

1、包引用

在gradle文件中引用retrofit

  1. compile 'com.squareup.retrofit2:retrofit:2.3.0'
  2.     compile 'com.squareup.retrofit2:retrofit-converters:2.3.0'
  3.     compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'

如果需要使用更多扩展功能,比如gson转换,rxjava适配等,可以视自己需要继续添加引用;

  1. compile 'com.squareup.retrofit2:converter-gson:2.3.0'
  2.     compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

如果现有的扩展包不能满足需要,还可以自己扩展converter,adapter等;

2、定义接口

Retrofit要求定义一个网络请求的接口,接口函数里要定义url路径、请求参数、返回类型;

  1. public interface INetApiService { 
  2.     @GET("/demobiz/api.php"
  3.     Call<BizEntity> getBizInfo(@Query("id") String id); 

在这个接口定义中,用注解@GET("/demobiz/api.php")声明了url路径,用注解@Query("id") 声明了请求参数;

最重要的是,用Call声明了返回值是一个Retrofit的Call对象,并且声明了这个对象处理的数据类型为BizEntity,BizEntity是我们自定义的数据模型;

3、依次获得Retrofit对象、接口实例对象、网络工作对象

首先,需要新建一个retrofit对象;

然后,根据上一步的接口,实现一个retrofit加工过的接口对象;

最后,调用接口函数,得到一个可以执行网络访问的网络工作对象;

  1. //新建一个Retrofit对象 
  2. Retrofit retrofit=new Retrofit.Builder() 
  3. .baseUrl(Config.DOMAIN)//要访问的网络地址域名,如http://www.zhihu.com 
  4. .addConverterFactory(GsonConverterFactory.create()) 
  5. .build(); 
  6. ... 
  7. //用retrofit加工出对应的接口实例对象 
  8. INetApiService netApiService= retrofit.create(INetApiService.class); 
  9. //可以继续加工出其他接口实例对象 
  10. IOtherService otherService= retrofit.create(IOtherService.class); 
  11. ··· 
  12. //调用接口函数,获得网络工作对象 
  13. Call<BizEntity> callWorker= netApiService.getBizInfo("id001"); 

这个复杂的过程下来,最终得到的callWorker对象,才可以执行网络访问。

4、访问网络数据

用上一步获取的worker对象,执行网络请求

  1. callWorker.enqueue(new Callback<BizEntity>() { 
  2.             @Override 
  3.             public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} 
  4.             @Override 
  5.             public void onFailure(Call<BizEntity> call, Throwable t) {...} 
  6.         }); 

在回调函数里,取得我们需要的BizEntity数据对象

二、Retrofit实现原理

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

1、Retrofit对象的构建就是简单的builder模式,直接看create

  1. //Retrofit.java 
  2. public <T> T create(final Class<T> service) { 
  3.     //验证 
  4.     validateServiceInterface(service); 
  5.     return (T) 
  6.         //动态代理 
  7.         Proxy.newProxyInstance( 
  8.         service.getClassLoader(), //类加载器 
  9.         new Class<?>[] {service}, //一组接口 
  10.         new InvocationHandler() { 
  11.             //判断android和jvm平台及其版本 
  12.             private final Platform platform = Platform.get(); 
  13.             @Override 
  14.             public Object invoke(Object proxy, Method method, Object[] args){ 
  15.                 //如果该方法是Object的方法,直接执行不用管 
  16.                 if (method.getDeclaringClass() == Object.class) { 
  17.                     return method.invoke(this, args); 
  18.                 } 
  19.                 //isDefaultMethod:检查是否是java8开始支持的接口默认方法 
  20.                 return platform.isDefaultMethod(method) 
  21.                     ? platform.invokeDefaultMethod(method, service, proxy, args) 
  22.                     : loadServiceMethod(method).invoke(args); //我们关注这里 
  23.             } 
  24.         }); 

Proxy.newProxyInstance动态代理,运行期会生成一个类(字节码)如$ProxyN,实现传入的接口即WanApi,重写接口方法然后转发给InvocationHandler的invoke

  1. class $ProxyN extends Proxy implements WanApi{ 
  2.     Call<WanArticleBean> articleList(@Path("page"int page){ 
  3.         //转发给invocationHandler 
  4.         invocationHandler.invoke(this,method,args); 
  5.     } 

2、validateServiceInterface验证逻辑

  1. //Retrofit.java 
  2. private void validateServiceInterface(Class<?> service) { 
  3.     //检查:WanApi不是接口就抛异常... 
  4.     //检查:WanApi不能有泛型参数,不能实现其他接口... 
  5.     if (validateEagerly) { //是否进行严格检查,默认关闭 
  6.         Platform platform = Platform.get(); 
  7.         for (Method method : service.getDeclaredMethods()) { //遍历WanApi方法 
  8.             //不是默认方法,并且不是静态方法 
  9.             if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { 
  10.                 //把方法提前加载进来(检查下有没有问题) 
  11.                 loadServiceMethod(method); 
  12.             } 
  13.         } 
  14.     } 

如果开了validateEagerly,会一次性把接口WanApi的所有方法都检查一遍并加载进来,可以在debug模式下开启,提前发现错误写法,比如在@GET请求设置了@Body这种错误就会抛出异常:

  1. java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body. 

3、loadServiceMethod

然后是loadServiceMethod(method).invoke(args),看名字可知是先找方法,然后执行

  1. //Retrofit.java 
  2. //缓存,用了线程安全ConcurrentHashMap 
  3. final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); 
  4. ServiceMethod<?> loadServiceMethod(Method method) { 
  5.     ServiceMethod<?> result = serviceMethodCache.get(method); 
  6.     //WanApi的articleList方法已缓存,直接返回 
  7.     if (result != nullreturn result; 
  8.     synchronized (serviceMethodCache) { 
  9.         result = serviceMethodCache.get(method); 
  10.         if (result == null) { 
  11.             //解析articleList的注解,创建ServiceMethod并缓存起来 
  12.             result = ServiceMethod.parseAnnotations(this, method); 
  13.             serviceMethodCache.put(method, result); 
  14.         } 
  15.     } 
  16.     return result; 

跟进ServiceMethod.parseAnnotations

  1. //ServiceMethod.java 
  2. static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) { 
  3.     //1. 
  4.     RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method); 
  5.     //检查:articleList方法返回类型不能用通配符和void... 
  6.     //2. 
  7.     return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); 

4、 RequestFactory.parseAnnotations

  1. //RequestFactory.java 
  2. static RequestFactory parseAnnotations(Retrofit retrofit, Method method) { 
  3.     return new Builder(retrofit, method).build(); 
  4. class Builder { 
  5.     RequestFactory build() { 
  6.         //解析方法注解如GET 
  7.         for (Annotation annotation : methodAnnotations) { 
  8.             parseMethodAnnotation(annotation); 
  9.         } 
  10.         //省略各种检查... 
  11.         //解析参数注解如Path 
  12.         int parameterCount = parameterAnnotationsArray.length; 
  13.         parameterHandlers = new ParameterHandler<?>[parameterCount]; 
  14.         for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) { 
  15.             parameterHandlers[p] = 
  16.                 parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter); 
  17.         } 
  18.         //省略各种检查... 
  19.         return new RequestFactory(this); 
  20.     } 

得到RequestFactory后, HttpServiceMethod.parseAnnotations,HttpServiceMethod负责适配和转换处理,将接口方法的调用调整为HTTP调用

  1. //HttpServiceMethod.java 
  2. //ResponseT响应类型如WanArticleBean,ReturnT返回类型如Call 
  3. static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations( 
  4.     Retrofit retrofit, Method method, RequestFactory requestFactory) { 
  5.     //省略kotlin协程逻辑... 
  6.     Annotation[] annotations = method.getAnnotations(); 
  7.     //遍历找到合适的适配器 
  8.     CallAdapter<ResponseT, ReturnT> callAdapter = 
  9.         createCallAdapter(retrofit, method, adapterType, annotations); 
  10.     //得到响应类型,如WanArticleBean 
  11.     Type responseType = callAdapter.responseType(); 
  12.     //遍历找到合适的转换器 
  13.     Converter<ResponseBody, ResponseT> responseConverter = 
  14.         createResponseConverter(retrofit, method, responseType); 
  15.     okhttp3.Call.Factory callFactory = retrofit.callFactory; 
  16.     return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); 

5、最终返回了一个CallAdapted,看到CallAdapted

  1. //CallAdapted extends HttpServiceMethod extends ServiceMethod 
  2. class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> { 
  3.     private final CallAdapter<ResponseT, ReturnT> callAdapter; 
  4.     CallAdapted( 
  5.         RequestFactory requestFactory, 
  6.         okhttp3.Call.Factory callFactory, 
  7.         Converter<ResponseBody, ResponseT> responseConverter, 
  8.         CallAdapter<ResponseT, ReturnT> callAdapter) { 
  9.         super(requestFactory, callFactory, responseConverter); 
  10.         this.callAdapter = callAdapter; 
  11.     } 
  12.     @Override 
  13.     protected ReturnT adapt(Call<ResponseT> call, Object[] args) { 
  14.         //适配器 
  15.         return callAdapter.adapt(call); 
  16.     } 

回到Retrofit.Builder

  1. //Retrofit.Builder.java 
  2. public Retrofit build() { 
  3.     Executor callbackExecutor = this.callbackExecutor; 
  4.     //如果没设置线程池,则给android平台设置一个默认的MainThreadExecutor(用Handler将回调切回主线程) 
  5.     if (callbackExecutor == null) { 
  6.         callbackExecutor = platform.defaultCallbackExecutor(); 
  7.     } 
  8.     List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); 
  9.     //添加默认的DefaultCallAdapterFactory 
  10.     callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); 

DefaultCallAdapterFactory这个工厂创建具体的CallAdapter实例

  1. //DefaultCallAdapterFactory.java 
  2. public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { 
  3.     final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType); 
  4.     //如果指定了SkipCallbackExecutor注解,就表示不需要切回主线程 
  5.     final Executor executor = 
  6.         Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) 
  7.         ? null
  8.         : callbackExecutor; 
  9.     return new CallAdapter<Object, Call<?>>() { 
  10.         @Override 
  11.         public Type responseType() { 
  12.             return responseType; 
  13.         } 
  14.         @Override 
  15.         public Call<Object> adapt(Call<Object> call) { 
  16.             //默认情况下,返回用主线程池包装的Call,他的enqueue会使用主线程池的execute
  17.             return executor == null ? call : new ExecutorCallbackCall<>(executor, call); 
  18.         } 
  19.     }; 

6、invoke

前边loadServiceMethod得到了CallAdapted,然后执行invoke,实现在父类HttpServiceMethod里,

  1. //HttpServiceMethod.java 
  2. final ReturnT invoke(Object[] args) { 
  3.     //终于见到okhttp了! 
  4.     Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter); 
  5.     return adapt(call, args); 
  6. class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> { 
  7.     private final CallAdapter<ResponseT, ReturnT> callAdapter; 
  8.     @Override 
  9.     protected ReturnT adapt(Call<ResponseT> call, Object[] args) { 
  10.         //用前边得到的适配器,把OkHttpCall包成ExecutorCallbackCall 
  11.         return callAdapter.adapt(call); 
  12.     } 

然后是请求入队,ExecutorCallbackCall.enqueue -> OkHttpCall.enqueue,

  1. //ExecutorCallbackCall.java 
  2. void enqueue(final Callback<T> callback) { 
  3.     delegate.enqueue( 
  4.         new Callback<T>() { 
  5.             @Override 
  6.             public void onResponse(Call<T> call, final Response<T> response) { 
  7.                 //将回调切回主线程 
  8.                 callbackExecutor.execute
  9.                     () -> { 
  10.                         callback.onResponse(ExecutorCallbackCall.this, response); 
  11.                     }); 
  12.                 //... 
  13.             } 
  14.             @Override 
  15.             public void onFailure(Call<T> call, final Throwable t) {} 
  16.         }); 
  17. //OkHttpCall.java 
  18. void enqueue(final Callback<T> callback) { 
  19.     //okhttp逻辑 
  20.     okhttp3.Call call; 
  21.     call.enqueue(new okhttp3.Callback() { 
  22.         void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) { 
  23.             callback.onResponse(OkHttpCall.this, response); 
  24.         } 
  25.     }) 

到此整个流程就通了

二、功能扩展

1、OkHttpClient

Retrofit使用OkHttpClient来实现网络请求,这个OkHttpClient虽然不能替换为其他的网络执行框架比如Volley,但是Retrofit允许我们使用自己扩展OkHttpClient,一般最常扩展的就是Interceptor拦截器了;

  1. OkHttpClient mClient = new OkHttpClient.Builder() 
  2.                 .addInterceptor(new Interceptor() { 
  3.                     @Override 
  4.                     public Response intercept(Chain chain) throws IOException { 
  5.                         try { 
  6.                             Request.Builder builder = chain.request().newBuilder(); 
  7.                             builder.addHeader("Accept-Charset""UTF-8"); 
  8.                             builder.addHeader("Accept"" application/json"); 
  9.                             builder.addHeader("Content-type""application/json"); 
  10.                             Request request = builder.build(); 
  11.                             return chain.proceed(request); 
  12.                         } catch (Exception e) { 
  13.                             e.printStackTrace(); 
  14.                         } 
  15.                         return null
  16.                     } 
  17.                 }).build(); 
  18. Retrofit retrofit = new Retrofit.Builder() 
  19.                 .baseUrl(Config.DOMAIN) 
  20.                 .addConverterFactory(GsonConverterFactory.create()) 
  21.                 .client(mClient) 
  22.                 .build(); 

2、addConverterFactory

扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另一种数据对象;

GsonConverterFactory可以把Http访问得到的json字符串转换为Java数据对象BizEntity,这个BizEntity是在INetApiService接口中要求的的;

如果现有的扩展包不能满足需要,可以继承Retrofit的接口。retrofit2.Converter

在创建Retrofit对象时,可以插入我们自定义的ConverterFactory;

  1. //retrofit对象 
  2. Retrofit retrofit=new Retrofit.Builder() 
  3. .baseUrl(Config.DOMAIN) 
  4. .addConverterFactory(GsonConverterFactory.create()) 
  5. .addConverterFactory(YourConverterFactory.create())//添加自定义Converter 
  6. .build(); 

3、addCallAdapterFactory

扩展的是对网络工作对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象;

这个转换不太好理解,我们可以对照下图来理解:

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

Retrofit本身用一个OkHttpCall的类负责处理网络请求,而我们在接口中定义需要定义很多种Call,接口里的Call和Retrofit里的OkHttpCall并不一致,所以我们需要用一个CallAdapter去做一个适配转换;

这其实是Retrofit非常核心,也非常好用的一个设计,如果我们在接口中要求的函数返回值是个RxJava的Flowable对象

  1. public interface INetApiService { 
  2.     @GET("/demobiz/api.php"
  3.     Flowable<BizEntity> getBizInfo(@Query("id") String id); 

那么我们只需要为Retrofit添加对应的扩展;

  1. //retrofit对象 
  2. Retrofit retrofit=new Retrofit.Builder() 
  3. .baseUrl(Config.DOMAIN) 
  4. .addConverterFactory(GsonConverterFactory.create()) 
  5. .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 
  6. .build(); 

就能得到Flowable类型的callWorker对象;

  1. //用retrofit加工出对应的接口实例对象 
  2. INetApiService netApiService= retrofit.create(INetApiService.class); 
  3. ··· 
  4. //调用接口函数,获得网络工作对象 
  5. Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001"); 
  6. 在这里,callAdapter做的事情就是把retrofit2.Call对象适配转换为Flowable<T>对象; 

同样,如果现有的扩展包不能满足需要,可以继承Retrofit的接口;retrofit2.CallAdapter

4、动态替换url

在构建Retrofit时传入HttpUrl对象,之后这个实例就一直存在不会更改,所以可以反射修改他的字段比如host,来实现动态替换服务端地址;

  1. String SERVER = "https://www.xxx.com/"
  2. HttpUrl httpUrl = HttpUrl.get(SERVER); 
  3. Retrofit retrofit = new Retrofit.Builder() 
  4.     //.baseUrl(SERVER) 
  5.     .baseUrl(httpUrl) //使用HttpUrl 
  6.     .build(); 

总结:

1.Retrofit将Http请求抽象成java接口

2.接口里用注解 描述和配置网络请求参数

3.动态代理的方式来生成call对象。

原文链接:https://mp.weixin.qq.com/s/diT6tn3EGO41r-b7TAJ8Yg

延伸 · 阅读

精彩推荐