Retrofit2 源码分析

将一个Java接口翻译成一个Http请求,然后用OkHttp去发送这个请求

一 入口类 Retrofit   成员变量

private final  HttpUrl baseUrl;//网络请求基地址

private final List<Converter.Factory>converterFactories;//数据转换器工厂集合

private final List<CallAdapter.Factory> adapterFactories;//网络请求适配器工厂集合

private final okhttp3.Call.Factory callFactory;//底层进行网络请求工厂

private final Executor callbackExecutor;//回调方法执行器

/ServiceMethod是对业务接口中方法的注解进行解析之后得到的对象,该对象包含了访问网络的除了方法参数值之外的所有必要信息;如数据转换器、网络请求适配器、网络请求工厂、基地址、Http方法等等。

private final Map<Method,ServiceMethod> serviceMethodCache =newLinkedHashMap<>();

Retrofit.bulid()方法 初始化 外观模式

二 动态代理

https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/ 动态代理的机制

http://www.ibm.com/developerworks/cn/java/j-jtp08305.html 动态代理的Decorator

GitHubServiceservice = retrofit.create(GitHubService.class);

create方法就是返回了一个Proxy.newProxyInstance动态代理对象

动态代理是java中的字节码生成技术。通过接口生成代理类,并将代理类的实现交给InvocationHandler作为具体的实现 ,

动态代理作为 Decorator 

就是你要调用某个class方法的前后 ,你可以很方便的插入些你想要执行的额外代码

一个动态代理类可以充当所有接口的 Decorator 或 Proxy,这样就不用每个接口都编写一个具体的实现类

Call <List<Repo>> repos = service.listRepos("octocat");

执行流程:

service对象其实是一个动态代理对象,并不是一个真正的GitHubService接口的implements产生的对象,当api对象调用listRepos方法时会被动态代理拦截,然后调用Proxy.newProxyInstance方法中的InvocationHandler对象的invoke方法。

invoke方法会传入3个参数:

Object proxy, 代理对象

Method method,调用的方法 listRepos

Object... args 方法参数 octocat

Retrofit里的动态代理比较巧妙。它不关心proxyd,只是单纯的为了拿到了这个method上所有的注解 生成ServiceMethod对象,生成的ServiceMethod对象和args构造生成了HttpCall  可以发起HTTP请求的类

总结:简化复杂的网络请求,通过动态代理的去处理解析与拼装HTTP请求


三 核心类 ServiceMethod

一个ServiceMethod对象对应于一个 API interface 的一个方法

主要成员变量

final okhttp3.Call.Factory callFactory; //负责创建 HTTP 请求

final CallAdapter callAdapter; //请求适配器  把retrofit2.Call<T>转为T

private final Converter responseConverter;  负责把服务器返回的数据(JSON、XML、二进制或者其他格式,由ResponseBody封装)转化为T类型的对象

private final Headers headers; //请求头信息

private final ParameterHandler[ ] parameterHandlers;//负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数

初始化过程 build()方法

callAdapter= createCallAdapter();//将遍历一个CallAdapter.Factory列表,根据方法返回值类型和注解提供需要的网络请求适配器

responseType=callAdapter.responseType();

responseConverter= createResponseConverter();//;即根据方法返回值类型和注释从retrofit中获取对应的转换器

for(Annotation annotation :methodAnnotations) {

parseMethodAnnotation(annotation);//解析Method的注解 给ServiceMethod的成员变量赋值

}

for(intp =0;p < parameterCount;p++) {

...

parameterHandlers[p] = parseParameter(p,parameterType,parameterAnnotations);

//方法中的每个参数创建一个ParameterHandler对象,该对象的创建过程就对方法参数中的Body、PartMap、Part、FieldMap、Field等注解进行解析

}

核心:okhttp3.Call.Factory,CallAdapter.Factory和Converter.Factory三个工厂类,模块之间、类之间通过接口进行依赖,创建十么样的实现类交给工厂去处理,工厂同样也是接口,添加怎样的工厂,则在最初构造Retrofit对象时决定,各个模块之间完全解耦,每个模块只负责自己的责任

四 执行HTTP请求

4.1 OkHttpCall类中execute() 同步发起网络请求,执行分析

createRawCall()    

1调用了serviceMethod.toRequest(args)来创建okhttp3.Request对象,之前解析参数注解的parameterHandlers在这里给HTTP请求 设置参数传进入,最后RequestBuilder build()生成Request请求

2 再调用serviceMethod.callFactory.newCall(request)来创建okhttp3.Call,这里之前准备好的callFactory同样也派上了用场,由于工厂在构造Retrofit对象时可以指定,所以我们也可以指定其他的工厂,来使用其它的底层 HttpClient 实现。这里Retrofit2就和Okhttp结合起来了 网络请求的执行业务流程都交给Okhttp来处理

return parseResponse(call.execute());

调用okhttp3.Call#execute()来执行网络请求,这个方法是阻塞的,执行完毕之后将返回收到的响应数据。收到响应数据之后,进行状态码的检查,再调用serviceMethod.toResponse(catchingBody)来把响应数据转化成我们需要的数据类型对象。使用之前准备好的responseConverte转化数据。

4.2 enqueue(Callback callback) 异步执行

这里的异步交给了okhttp3.Call#enqueue(Callback responseCallback)来实现,并在它的 callback 中调用parseResponse解析响应数据,并转发给传入的 callback

五  CallAdapter 接口

请求的适配化OkHttpCall --Adapter--> RxJava/java8/UICallback

通过适配器实现OkHttpCall到其它类型(比如RxJava等第三方库)的适配转换

public Interface CallAdapter<T> {

Type responseType();//该请求适配器返回的数据类型

 T adapt(Call call);//该请求适配器对原始Call的再次封装,如Call到Observable,

abstract class  Factory {

public abstract CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit);//获取网络请求适配器

protected    staticType getParameterUpperBound(intindex, ParameterizedType type) 

{returnUtils.getParameterUpperBound(index, type); }

protectedstaticClass getRawType(Type type) {returnUtils.getRawType(type); }

}

ExecutorCallAdapterFactory 是Retrofit默认实现  

该对象存储一个回调执行器,异步请求将相应的结果交给callbackExecutor回调执行器去执行Android平台Retrofit2会使用主线程handler构造一个ExecutorCallAdapterFactory,调用enqueue(Callback),callback回调会在主线程中回调

5.1 Retrofit2和RxJava的结合使用

1 RxJavaCallAdapterFactory类 

 RxJava对请求进行包装,它将根据网络请求生成一个Observable进行流式任务执行

getCallAdapter方法中对返回值的泛型类型进行了进一步检查,例如我们声明的返回值类型为Observable,泛型类型就是List,这里对retrofit2.Response和retrofit2.adapter.rxjava.Result进行了特殊处理,有单独的 adapter 负责进行转换,其他所有类型都由SimpleCallAdapter负责转换

2 SimpleCallAdapter#adapt方法

创建了一个Observable,传入了CallOnSubscribe类,同时使用了一个OperatorMapResponseToBodyOrError操作符,用来把retrofit2.Response转为我们声明的类型,或者错误异常类型

3 CallOnSubscribe#call方法

clone 了原来的 call,因为okhttp3.Call是只能用一次的,所以每次都是新 clone 一个进行网络请求;创建了一个叫RequestArbiter的 producer,把这个 producer 设置给 subscriber;

Producer机制 简单的理解

Subscriber 都是被动接收 Observable 传递 过来的数据,然后Subscriber做处理。

但要是 Observable 发得太多,Subscriber 处理不过来,那就有问题了,所以就有了一种 Subscriber 主动 pull 的机制,而这种机制就是通过 Producer 实现的。给 Subscriber 设置 Producer 之后(通过Subscriber#setProducer方法),Subscriber 就会通过 Producer 向上游根据自己的能力请求数据(通过Producer#request方法),而 Observable 收到请求之后再根据请求的量给 Subscriber 发数据。

RequestArbiter#request方法

执行call.execute() 获取到Response响应数据,并且发送给下游接收

Observable.lift(OperatorMapResponseToBodyOrError.instance())

lift操作符 负责接收原始的Observable发出的事件,并在response.body()并发送给下游。这里,body()返回的就是我们声明的泛型类型了

Call<R>->Response<T>->Observable<T>  执行流程

1 Observable.subscribe,触发 API 调用的执行;

2 CallOnSubscribe#call,clone call,创建并设置 producer;

3 subscriber 被设置了 producer 之后调用RequestArbiter#request,在 request 中发起网络请求,把处理结果发给下游;

4 OperatorMapResponseToBodyOrError$1#onNext,把 response 的  body 发给下游

5 最终就到了我们 subscribe 时传入的回调里面了;

六    概括Retrofit2的话,借鉴了服务器编程中AOP思想,利用动态代理技术通过接口在运行时拦截方法,接着通过解析注解拼装HTTP请求;最后包装了OkHttpCall生成真正的请求类 发起网络请求, 通过抽象工厂让各个模块直接解耦,完成对原数据Respone的Conver,实现了对Rx、线程的Adapter ,

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容