参考:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/
以前使用的是android-async-http作为网络访问库,最近改用了Retrofit2,本文简单的分析下Retrofit2的源代码。
什么是Retrofit2
来自Retrofit官网的介绍:
A type-safe HTTP client for Android and Java
Retrofit 可以说是目前最流行的类型安全网络库,是Square公司的一个开源项目,通过注解来创建restful API接口。目前2.0版本已经稳定,下文如无特殊说明,均指2.0版本。
Retrofit2使用步骤
Retrofit2的使用很简单,主要分为以下几个步骤:
创建Interface接口,通过注解标识客户端与后台约定的API接口
1
2
3
4public interface GitHubService {
"users/{user}/repos") (
Call<List<Repo>> listRepos("user") String user); (
}创建Retrofit实例,生成Interface的实现类(动态代理实现对象)
1
2
3
4Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);调用上面创建的实现类中的API方法,取得Call对象
1
Call<List<Repo>> repos = service.listRepos("octocat");
根据调用场景异步或同步,执行Call对象的异步或同步方法,异步方法需要传入Callback回调函数
- 异步:call.enqueue(Callback callback)
- 同步:call.execute()
Retrofit2内部原理
- 第一步是根据跟服务器的接口协议文档写注解接口。(Retrofit非常巧妙的用注解来描述一个HTTP请求)
第二步根据接口创建相关执行代理对象。
1
2
3
4
5// The Retrofit class generates an implementation of the GitHubService interface.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);给Retrofit对象传了一个
GitHubService
接口的Class对象,怎么返回一个GitHubService
的对象呢?我们来看看create
方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// Single-interface proxy creation guarded by parameter safety.
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();
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);
}
});
}
create
方法重要就是返回了一个动态代理对象。
动态代理是个什么东西?
Java 动态代理就是Java开发给了开发人员一种可能:当你要调用某个类的方法前,插入你想要执行的代码。
比如你要执行某个操作前,你必须要判断这个用户是否登录。
为什么要使用动态代理
你看上面的代码,获取数据的代码就是这句:
1
2// Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.
Call<List<Repo>> repos = service.listRepos("octocat");
上面的service
对象其实是一个动态代理对象,并不是一个真正的GitHubService
接口的implements对象,当service
对象调用listRepos
方法时,执行的是动态代理方法,此时,动态代理发挥了它的作用,你看上去是调用了listRepos
方法,其实此时Retrofit把GitHubService
接口翻译成一个HTTP请求,也就是Retrofit中的MethodHandler对象,这个对象包含了:
1) OkHttpClient:发送网络请求的工具
2) RequestFactory:包含了HTTP请求的URL、Header信息,MediaType、Method以及RequestAction数组
3) CallAdapter:HTTP请求返回数据的类型
4) Converter:数据转换器
- 第三步Retrofit在你调用
Call<List<Repo>> repos = service.listRepos("octocat");
后为你生成了一个Http请求, - 第四步你调用
call.enqueue
方法时就发送了这个请求,然后你就可以处理Response的数据了
从原来上讲,就是这样的。
Retrofit2源码分析
想要弄清楚Retrofit的细节,先来看一下Retrofit源码的组成:
- 一个
retrofit2.http
包,里面全部是定义HTTP请求的注解,比如GET
、POST
、PUT
、DELETE
、Headers
、Path
、Query
等等 - 余下的
retrofit2
包中十几个类和接口就是全部retrofit的代码了,代码不是很多,retrofit把网络请求这部分功能全部交给了OkHttp了
Retrofit2接口
下面几个接口需要了解一下
Callback<T>
这个接口是retrofit请求数据返回的接口,有下面两个方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public interface Callback<T> {
/**
* Invoked for a received HTTP response.
* <p>
* Note: An HTTP response may still indicate an application-level failure such as a 404 or 500.
* Call {@link Response#isSuccess()} to determine if the response indicates success.
*/
void onResponse(Call<T> call, Response<T> response);
/**
* Invoked when a network exception occurred talking to the server or when an unexpected
* exception occurred creating the request or processing the response.
*/
void onFailure(Call<T> call, Throwable t);
}
Converter<F, T>
这个接口主要的作用就是讲HTTP返回的数据解析成Java对象,主要有Xml、Gson等等,我们可以在创建Retrofit对象时添加需要使用的Converter
实现1
2
3
4Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
Call<T>
这个接口主要的作用是发送一个HTTP请求,Retrofit默认的实现是OkHttpCall<T>
,我们也可以根据实际情况实现自己的Call类。
CallAdater<T>
CallAdapter
中属性只有 responseType
一个,还有一个<R> T adapter(Call<R> call)
方法,这个接口的实现类也只有一个,DefaultCallAdapter
。这个方法的主要作用就是将Call
对象转换成另一个对象,可能是为了支持RxJava才设计的这个类的吧。
Retrofit2的运行
上面分析到GitHubService service = retrofit.create(GitHubService.class);
代码返回了一个动态代理对象,而执行Call<List<Repo>> repos = service.listRepos("octocat");
代码返回了一个OkHttpCall
对象,拿到这个Call对象才能执行HTTP请求。
其中后一句代码执行了一个非常复杂的过程
当执行listRepos
方法时,Retrofit其实是执行了动态代理的InvocationHandler
对象,最后会创建一个MethodHandler
对象,这个对象很重要1
2
3
4
5
6
7
8
9
10
11
12
13
14static MethodHandler create(Retrofit retrofit, Method method) {
CallAdapter<?> callAdapter = createCallAdapter(method, retrofit);
Type responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw Utils.methodError(method, "'"
+ Types.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
Converter<ResponseBody, ?> responseConverter =
createResponseConverter(method, retrofit, responseType);
RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
return new MethodHandler(retrofit.callFactory(), requestFactory, callAdapter,
responseConverter);
}
上面的代码就是创建一个MethodHandler
对象,一个MethodHandler
对象中包含了4个对象
1. OkHttpClient
这个是Retrofit默认生成的
2. RequestFactory
通过RequestFactory requestFactory = RequestFactoryParser.parse(method, responseType, retrofit);
生成,主要作用就是解析整个Http请求的数据
主要原料是解析一个接口,比如上面的GitHubService
接口,得到整个Http请求的全部信息,还会通过@Path
注解拼接Url.
3. CallAdapter
获取CallAdapter
的代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private static CallAdapter<?> createCallAdapter(Method method, Retrofit retrofit) {
Type returnType = method.getGenericReturnType();
if (Utils.hasUnresolvableType(returnType)) {
throw Utils.methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw Utils.methodError(method, "Service methods cannot return void.");
}
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw Utils.methodError(e, method, "Unable to create call adapter for %s", returnType);
}
}
我们可以在创建Retrofit对象时,添加想要的CallAdapter
,获取CallAdapter
的方式也是从Retrofit
对象中获取
4. Converter
获得Converter对象和上面的原来类似1
2
3
4
5
6
7
8
9private static Converter<ResponseBody, ?> createResponseConverter(Method method,
Retrofit retrofit, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw Utils.methodError(e, method, "Unable to create converter for %s", responseType);
}
}
创建这4个对象的目的就是为了执行下面这句代码1
2
3
4Object invoke(Object... args) {
return callAdapter.adapt(
new OkHttpCall<>(callFactory, requestFactory, args, responseConverter));
}
这个也就是Call<List<Repo>> repos = service.listRepos("octocat");
返回的Call
对象。
最后调用Call
对象的execute
或enqueue(Callback<T> callback)
方法,就能发送一个Http请求了。Retrofit提供了同步和异步两种HTTP请求方式。
如何在Retrofit中使用RxJava
RxJava是由Netflix开发的响应式扩展(Reactive Extensions)的Java实现。由于Retrofit设计的扩展性非常强,我们只需要改变一下CallAdapter
就可以了1
2
3
4
5
6Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(ProtoConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
上面代码创建了一个Retrofit
对象,支持Proto和Gson两种数据格式,并且还支持RxJava
最后
Retrofit非常巧妙的用注解来描述一个HTTP请求,将一个HTTP请求抽象成一个Java接口,然后用了动态代理方式,动态的将这个接口注解“翻译”成一个HTTP请求,最后再执行这个HTTP请求。