文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-12-14 00:44

关注

前言

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 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 callWorker= netApiService.getBizInfo("id001"); 

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

4、访问网络数据

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

callWorker.enqueue(new Callback() {             @Override             public void onResponse(Call call, Response response) {...}             @Override             public void onFailure(Call call, Throwable t) {...}         }); 

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

二、Retrofit实现原理

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

//Retrofit.java public  T create(final Class 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 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> 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  ServiceMethod 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  HttpServiceMethod parseAnnotations(     Retrofit retrofit, Method method, RequestFactory requestFactory) {     //省略kotlin协程逻辑...     Annotation[] annotations = method.getAnnotations();     //遍历找到合适的适配器     CallAdapter callAdapter =         createCallAdapter(retrofit, method, adapterType, annotations);     //得到响应类型,如WanArticleBean     Type responseType = callAdapter.responseType();     //遍历找到合适的转换器     Converter 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 extends 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;     //如果没设置线程池,则给android平台设置一个默认的MainThreadExecutor(用Handler将回调切回主线程)     if (callbackExecutor == null) {         callbackExecutor = platform.defaultCallbackExecutor();     }     List 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>() {         @Override         public Type responseType() {             return responseType;         }         @Override         public Call adapt(Call 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 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);     } } 

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

//ExecutorCallbackCall.java void enqueue(final Callback callback) {     delegate.enqueue(         new Callback() {             @Override             public void onResponse(Call call, final Response response) {                 //将回调切回主线程                 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);         }     }) } 

到此整个流程就通了

二、功能扩展

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 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 callWorker= netApiService.getBizInfo("id001"); 在这里,callAdapter做的事情就是把retrofit2.Call对象适配转换为Flowable对象; 

同样,如果现有的扩展包不能满足需要,可以继承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对象。


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
咦!没有更多了?去看看其它编程学习网 内容吧