最近新上了一个项目,用到了 Retrofit 2 + RxJava 与服务器进行交互。使用了一段时间后,对于其 Retrofit 的简洁、灵活印象深刻。尤其好奇的是其定义一个 HTTP 请求的方式,仅仅通过一个接口定义加上几个注解就完成了。虽然能大概推测到,一定是通过什么方法 将接口方法和注解包含的信息转化成了一个 HTTP Request ,但是对于背后具体的实现还是不清楚。于是就抽空研究了一下源码,发现代码量不大,但是设计思想真是精妙,值得好好学习一下。
为了让讲解更加的具体,将 用一个项目中实际用到的接口作为示例 一步步的进行分析,看看 Retrofit 是如何进行一个 HTTP 请求的。当然了,因为实际的 HTTP
请求处理是依赖于 okhttp 的,所以这一部分就不深入讲解了,以后有时间再单独开一篇来讲 okhttp 吧。
0. Retrofit 项目结构
- http包:定义各种
@Get
,@Post
等注解; - 两个核心类:
Retrofit
,MethodHandler
。
Retrofit 的主要逻辑和配置基本上通过这两个类来完成,所以后面的分析也主要围绕着两个类展开。其他的类涉及到的时候再简单介绍
1. 示例接口
public interface AppApi {
/**
* 根据用户id返回用户基本信息
* @param uid 用户id
* @return
*/
@GET("api/v1/user/{uid}/")
Observable<User> getUser( @Path("uid") String uid );
}
这个接口功能非常简单,以用户id uid
作为参数发起请求,服务器会返回一个 json 字符串,这个字符串将被自动解析成一个 User
对象。当然前面提到用到了 RxJava,所以返回被转换成了一个可以产生 User
对象的 Observable
,不过并不影响我们的分析。
2. 使用前的基本配置
使用 Retrofit 发起调用前的一些初始化配置如下。
OkHttpClient httpClient = new OkHttpClient.Builder().build();
Retrofit appRetrofit = new Retrofit.Builder()
.client(httpClient)
.baseUrl(BuildConfig.API_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
发起一个 HTTP 请求
AppApi appApi = appRetrofit.create(AppApi.class);
Observable<User> observable = appApi.getUser("1234567");
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<User>() {
@Override
public void call(User user) {
//do something with user
}
});
上面这段代码的头两句背后的逻辑就是 Retrofit 的核心所在,接下来我们就一步步进行分析。
3. 创建动态代理对象
在上一节中,发起 HTTP 请求的第一句, 只调用了一个 Retrofit.create()
方法。
AppApi appApi = appRetrofit.create(AppApi.class);
Retrofit.create()
方法的源码如下:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadMethodHandler(method).invoke(args);
}
});
}
通过源码我们可以看到,这里实际的返回是通过
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)
构造出来的一个 动态代理对象。这个方法前两个参数没什么好书偶的,第一个参数指定加载代理对象的 ClassLoader
, 第二个参数就是需要被动态代理的接口数组,重点是第三个参数,它是一个 InvocationHandler
接口,需要实现它的 invoke()
方法。当通过动态代理对象调用接口的方法时,实际上最终就会调用 invoke()
方法。
关于 动态代理 的详细介绍可以参考这篇文章。这里引用其中关于动态代理的一个概述:
Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。
如果一时还无法理解动态代理的概念,那么现在只需要记住两点:
-
AppApi appApi = appRetrofit.create(AppApi.class);
这里的appApi
已经是一个动态代理对象; -
appApi.getUser("1234567");
动态代理对象调用的方法,最终都会去到InvocationHandler
的invoke()
方法里。
那么再回到 Retrofit.create()
方法,我们重点要关注的就是 InvocationHandler.invoke()
方法的实现。前面两个 if 判断可以直接略过,关键就在最后一句 loadMethodHandler(method).invoke(args)
。
loadMethodHandler()
方法的源码如下:
MethodHandler loadMethodHandler(Method method) {
MethodHandler handler;
synchronized (methodHandlerCache) {
handler = methodHandlerCache.get(method);
if (handler == null) {
handler = MethodHandler.create(this, method);
methodHandlerCache.put(method, handler);
}
}
return handler;
}
这个方法就是为我们的接口中的每一个方法生成一个 MethodHandler
。methodHandlerCache
就是一个用来缓存 MethodHandler
对象的 Map
,对于已经调用过的 method
直接从 methodHandlerCache
中取。
可以看到关键是 MethodHandler.create(this, method)
这句,生成了MethodHandler
对象。接下来就来分析 MethodHandler
的源码。
4. MethodHandler
首先要了解下 MethodHandler
类包含的几个属性:
-
okhttp3.Call.Factory
callFactory
:
okhttp
库中定义的接口,用来获得一个okhttp.Call
对象。callFactory
在默认情况下被初始化为一个OkHttpClient
; -
RequestFactory
requestFactory
:
包含一个create()
方法,根据各种参数,构造一个okhttp.Request
-
CallAdapter<?>
callAdapter
:
Retrofit
库中定义的接口,用来把一个okhttp.Call
对象转化为其他类型。比如Retrofit.Builder.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
就配置了一个RxJavaCallAdapterFactory
, 把一个Call
转换为Observable
; -
Converter<ResponseBody, ?>
responseConverter
:
Retrofit
库中定义的接口,用来解析 HTTP 响应和请求。Retrofit.Builder.addConverterFactory(GsonConverterFactory.create())
就配置了一个GsonConverterFactory
,可以把一个 json 格式的字符串响应转化为相应的对象。默认情况下会配置一个BuiltInConverters
,只是简单的转换为字符串。
介绍完几个关键属性,再来看 MethodHandler.create(Retrofit retrofit, Method method)
方法:
1. static MethodHandler create(Retrofit retrofit, Method method) {
2. CallAdapter<?> callAdapter = createCallAdapter(method, retrofit);
3. Type responseType = callAdapter.responseType();
4. if (responseType == Response.class || responseType == okhttp3.Response.class) {
5. throw Utils.methodError(method, "'"
6. + Types.getRawType(responseType).getName()
7. + "' is not a valid response body type. Did you mean ResponseBody?");
8. }
9. Converter<ResponseBody, ?> responseConverter = createResponseConverter(method, retrofit, responseType);
10. RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
11. return new MethodHandler(retrofit.callFactory(), requestFactory, callAdapter, responseConverter);
12. }
可以发现就是根据传入的 retrofit
参数 和 method
参数来完成了前面提到的几个属性的初始化, nothing special. 唯一需要注意的是 第10行:
RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);```
这里的 `RequestFactoryParser` 就是用来根据方法的注解、方法的参数等来解析出构造 `Request` 需要的信息,比如请求头部,请求方法,请求参数等。比较简单,就不深入了。
到这里,回顾一下我们的调用链:
1. Retrofit.create() 构造动态代理对象;(` AppApi appApi = appRetrofit.create(AppApi.class);`)
2. 动态代理对象调用方法 (`appApi.getUser("1234567");`)
3. 方法调用实际被转化为 `InvocationHandler.invoke()`
4. `loadMethodHandler()` 构造一个 `MethodHandler`
现在,离最终的调用只剩最后一步了 就是 `MethodHandler.invoke()` ,其实这个方法只有一句话...
```java
Object invoke(Object... args) {
return callAdapter.adapt(new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}
在这里终于见到了 HTTP 调用的真身 OkHttpCall
, 而 callAdapter 对于我们的例子来说就是
RxJavaCallAdapterFactory.ResponseCallAdapter
, 其定义的 adapt()
方法会把 OkHttpCall
转化为 Observable
,这点本节开头已经讲过。
至此,一个从定义API接口到生成API请求的完整流程就走完了。不过要注意下,这里实际上还没有发起 HTTP 请求,只是准备好了发起一个 HTTP 请求所需要的一切。实际的请求在调用 OkHttpCall.execute() 或 enqueue()
的时候才会发起 ( 对于 Observable 类型的,是在Observable.subscribe()
的时候才发起 )
5. 结语
通过上面的分析可以看到,动态代理是 Retrofit 的核心所在,大量使用 Factory 模式使得 Retrofit 具备了模块化,可灵活配置的能力,而通过注解定义 HTTP 请求,使得代码可读性变得更高。真的是一旦上手,别无所求啊!