retrofit有几个关键的地方.
1.用户自定义的接口和接口方法.(由动态代理创建对象.)
2.converter转换器.(把response转换为一个具体的对象)
3.注解的使用.
让我们跟随Api来看吧.
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(API_URL).build();
build()其内部实现是这样的:
[代码]java代码:
1 2 3 4 5 6 7 8 |
|
当用户没有设置自定义的converter,client, httpExecutor(http访问执行的线程–>只对异步的retrofit有效.), callBackExecutor(异步的callBack执行的线程), errorHandler, log, RequestInterceptor的时候,就会使用retrofit默认的配置.调用 ensureSaneDefaults();
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
可以看到进行初始化的时候调用了Platform.get()。
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 |
|
使用了单例的PLATFORM,通过findPlatform()初始化实例, 由于retrofit支持不同的平台,Platform用于判断使用的是哪个平台,如果是Android平台就使用Platform.Android,retrofit的Android类继承了Platform, 根据android的特性对配置项做了处理.如果是Google AppEngine就使用Platform.AppEngine,否则使用Platform.Base,这些都是Platform的子类,其中AppEngine又是Base的子类。 Platform是一个抽象类,定义了以下几个抽象方法,这几个方法的作用就是返回一些RestAdapter中需要要用到成员的默认实现
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
其中判断了是否有okHttp的路径
[代码]java代码:
1 2 3 4 5 6 7 8 |
|
可以发现上面默认的Http的Executor是一个线程池.
而CallBack的Executor是在主线程执行的. 由绑定MainLooper的Handler提交到主线程执行.
[代码]java代码:
1 2 3 4 5 6 |
|
[代码]java代码:
1 2 3 4 |
|
[代码]java代码:
1 2 3 4 5 |
|
现在有下面一个接口,
[代码]java代码:
1 2 3 4 |
|
下面了解下 SimplePOST simplePost= adapter.create(SimplePOST.class)的内部逻辑.
[代码]java代码:
1 2 3 4 5 |
|
[代码]java代码:
1 2 3 4 5 6 7 8 |
|
可以发现adapter.create()方法的内部实现是利用动态代理生成了service接口的一个实现类. 根据动态代理的原理. 可以得知调用实现类的方法其实就是调用InvocationtHandler的对应方法. 虽然这里是运用了动态代理的技术.但是却和一般的动态代理不一样. 一般的动态代理的InvocationHandler应该通过构造函数中传入委托类A.然后在invoke方法中调用A的方法, 但这里是没有委托类的.只是利用动态代理自动生成接口的实现类.
因为java的动态代理是基于接口的,所以retrofit也要求用户自定义的也必须是一个接口.
注意invocationHandler的invoke()方法执行是在我们调用接口的方法的时候执行的.对于上面的代码就simplepost.getResponse()执行的时候.
所以上面的代码先对传入的Class 进行校验:Utils.validateServiceClass(service),必须是接口,并且不能是一个没有自己函数并继承自其他父接口的空接口。 校验完成后返回一个动态代理类,我们想要知道retrofit使用用户自定义的接口干了什么事,就需要查看new RestHandler(getMethodInfoCache(service)) 的具体实现。
RestHandler 继承自 InvocationHandler 实现它的 invoke 函数,当代理类的接口函数被调用时,会先调用代理类的invoke 函数,然后在invoke 函数里通过反射调用用户指定的接口函数。
查看invoke 函数的具体实现之间,我们先分析分析getMethodInfoCache(service)函数。
下面是getMethodInfoCache函数的实现部分
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 |
|
该函数使用了同步代码块 synchronized 以serviceMethodInfoCache为对象锁,防止其他线程在该函数执行的时候入侵,之后在serviceMethodInfoCache查询service是否在其中存在,如果不存在,就新new 一个LinkedHashMap<Method, RestMethodInfo>加入其中,RestMethodInfo是一个final 类型的class,下面是它的申明
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
|
从RestMethodInfo类的注释来看,它和用户自定义的接口有密切的关系,也就是说,用户接口中定义的函数,由该类来解析说明和发出请求。
之后就将methodDetailsCache(LinkedHashMap<Method, RestMethodInfo>类型)传入到RestHandler类的构造方法当中,为其的同名属性完成赋值的同时返回该类的实例。接下来我们就开始分析RestHandler中的invoke 函数,这可以说是retrofit的精髓所在。
[代码]java代码:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
|
下面来分析每一步的调用流程。 (一)判断call的method是不是属于当前代理Object申明的,如果是就返回方法调用后的结果而不继续向下执行。
(二)调用getMethodInfo函数 传入参数为调用RestHandler构造方法时传入的methodDetailsCache(LinkedHashMap<Method, RestMethodInfo>类型)和具体调用的method(Method类型),返回值是一个RestMethodInfo的实例。getMethodInfo函数主要作用是将用户调用的method解析为一个RestMethodInfo实例,然后再将method和该RestMethodInfo实例以key-value键值对的形式绑定,存放在methodDetailsCache中,然后再将该RestMethodInfo实例返回。(ps: RestMethodInfo中的对Mehod接口解析是很重要的,但是这里作者就一笔带过了,读者自己看看吧,就是JAVA反射那套东西,还是很简单的)
(三)调用并返回invokeRequest函数 这里拿到了将method解析后的methodInfo(RestMethodInfo类型),判断该调用函数用户是想让它同步执行还是异步执行(利用RestMethodInfo中的isSynchronous属性判断),同步执行则进入到if判断,调用并返回invokeRequest函数.
第一个参数RequestInterceptor接口是一个请求拦截器,能在请求执行之前向请求添加一些额外的数据,在最开始分析RestAdapter 创建的时候分析到ensureSaneDefaults 函数,如果没有在创建RestAdapter的时候传入自己实现的RequestInterceptor,那么Retrofit会给一个默认的RequestInterceptor空实现,指向RequestInterceptor接口类中的NONE属性。第二个参数是刚才Method解析后的RestMethodInfo,第三个参数是用户调用函数时传入的参数数组。
methodInfo.init() 这个函数调用之后会将当前用户调用的方法进行进一步的解析,比如对@POST @GET @HEAD…etc 这些annotation的解析,并且每个实例只解析一次。 RequestBuilder requestBuilder = new RequestBuilder(serverUrl, methodInfo, converter); 从字面字面意思看,这个类和请求有关。确实,这个类里包含了用户请求网络所包含的各种参数,比如Host地址,Http协议头…etc 反正就是组成一个http请求所必要的东西。 Request request = requestBuilder.build(); 由requestBuilder调用build函数产生一个Request类,该类是一个常量实体类,包含了最少的请求Http时所需要的信息。之后是请求信息的日志打印,在接下来关键的地方来了。
[代码]java代码:
1 2 3 |
|
start 表示请求开始的时间,elapsedTime 表示该次请求花费的时间,而中间那句则是进行真正的网络请求,clientProvider 表示你在前面创建RestAdapter 时调调用ensureSaneDefaults函数所初始化的网络请求客户端(可能是OkClient, AndroidApacheClient,UrlFetchClient或者UrlConnectionClient四个的其中之一)。执行完成后返回一个response 这是一个Response常量实体类,和前面提到的Request常量实体类对应(一个表示请求实体,一个表示相应实体),里面存放着该次请求返回的Http协议头,状态码,body…etc .
在请求之前还有一个profiler
[代码]java代码:
1 2 3 4 |
|
这里用到了AOP(面向切面)设计模式,如果用户设置了profiler的话,在请求的前后分别会调用profiler的beforeCall和afterCall函数,以通知用户请求的完成情况(在afterCall函数中传递了响应结果)。 之后的代码就是对response 的一个处理了,如果响应结果不在200到300之间,Retrofit会抛出一个自定义的异常进行进一步的处理,如果在200到300之间则会对response的请求数据做进一步的处理 .比如调用new ResponseWrapper(response, convert);利用converter(Converter接口类型,但实际上是GsonConverter,对Gson的一个封装) 将数据解析为用户想要返回的那个类型。
如果判断用户不是希望同步执行,那么invokeRequest函数的执行将会在之后执行。首先,会判断你是否使用了RxJava。如果使用了RxJava ,则组建一个RxSupport(对RxJava 中类的简单封装),使用它的createRequestObservable函数对invokeRequest函数进行异步调用并返回一个ResponseWrapper。否则使用httpExecutor(前面初始化的时候被设置为CachedThreadPool(缓存线程池))对invokeRequest函数进行异步调用并返回一个ResponseWrapper。
最后看看CallbackRunnable的实现
[代码]java代码:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
就是一个普通的Runnable,在run方法中首先执行obtailResponse,从名字可以看到是执行请求返回Response,这个从前面可以看到执行了invokeRequest,和同步调用中一样执行请求。 紧接着就提交了一个Runnable至callbackExecutor,在看Platform时看到了callbackExecotor是通过Platform.get().defaultCallbackExecutor()返回的,Android中是向主线程的一个Handler发消息
值得注意的事,对于同步调用,如果遇到错误是直接抛异常,而对于异步调用,是调用Callback.failure()
retrofit的大体流程
用户自定义配置项的设置(如client,converter,拦截器等)—>解析接口的方法(如果曾经解析过就从缓存中获取),确定http访问的url,header,method等,确定是异步还是同步的方式——>使用具体的Client进行网络访问,并将数据封装到Response—->执行Converter的逻辑(有可能不用执行),把Response数据转换为一个具体对象.—>根据同步或者异步的方式,执行方法或者callBack的逻辑. ### retrofit框架的需要注意的几个小点.
1.为什么同步方式不像正常的方式一样要求用户try_catch来提醒用户捕捉异常?
通过上面的逻辑可以看到,真正进行网络访问,converter转换的逻辑都在invokeHandler.invoke()方法执行的时候执行. 而这个方法的调用是在 用户自定义接口调用接口方法的时候执行的.(不明白的可以看下动态代理的原理).而用户自定义的接口方法是没有抛出异常的.在java中,如果父类方法没有抛出异常,子类方法也不能显示的抛出异常.(子类方法只能抛出父类方法抛出异常或其子类).所以Retrofit就不能抛出各种异常(如IO异常). 并且要抓住异常后转换为RuntimeException抛出.(动态代理生成的接口的实现类其实内部也采用了同样的方法.)
异常抓住后不能直接内部处理,应该提醒用户代码执行的时候出了问题,所以必须抓住异常后再次抛出.而对于CallBack的方式,因为有failure()方法提示用户代码逻辑出了问题,所以就不用re-throw异常了.
2.关于 InvocationHandler的invoke()方法, 这个方法有个返回值. 那这个返回值返回的是什么呢?
首先明确Method.invoke(Object receiver,Object.. args)是和 receiver.method(args)等价的,2个方法的返回值是一样的.
public Object invoke(Object receiver, Object… args) 这里的Object是方法执行的的返回值. —> Returns the result of dynamically invoking this method. Equivalent to {@code receiver.methodName(arg1, arg2, … , argN)}. 动态代理生成了接口A的代理类B,B的同名方法内部其实调用的是invocationHandler的 invoke()方法.返回的也是invoke方法的返回值. 所以invoke返回的类型就应该和接口方法的返回值类型一样