今天来分析Retrofit2的源码。(2.3版本)
这里使用了一个关于获取github的用户信息的示例,源码在:
示例源码展示
首先要知道Retrofit是通过动态代理的方式创建各种网络接口的代理。
至于动态代理这里就不做阐述了。
而且不仅仅是动态代理模式,还有许许多多的设计模式,先来看看Retrofit的设计模式
我们通过其使用流程来观察其代码的使用:
1、创建Retrofit对象
Retrofit的使用从创建Retrofit对象开始:
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
我们通过建造者来创建一个Retrofit对象,主要关注build()中的实现:
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//设置Call的工厂类,如果没有则为OkhttpClient对象
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
//设置方法回调的执行者,在Android平台 执行者会在主线程执行回调方法。
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
//适配器工厂集合
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
//转化器工厂类集合,用于解析网络响应
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
在build方法中会初始化Call的工厂类,默认实现为OkHttpClient。
接着初始化网络方法的执行器集合和适配器工厂集合,这两者分别通过platform来往里面添加一个默认的网络回调执行器,而platform为Android类的对象,代码如下:
//Android平台(默认回调在主线程执行)
static class Android extends Platform {
//返回回调方法的执行器MainThreadExcutor
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
//返回ExecutorCallAdapterFactory对象
@Override
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
//主线程执行器
static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}
Android中默认的网络回调执行器为MainThreadExecutor,与主线程Looper的Handler绑定,将网络回调推送到主线程执行。
默认的CallAdapter.Factory为ExecutorCallAdapterFactory对象。CallAdapter.Factory主要适配接口的返回类型,如listRepo的接口方法为:
public interface GitHubService{
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
返回为Call< T>类型,Call< T>类型是由ExecutorCallAdapterFactory来适配的,如果在创建Retrofit对象时配置使用RxJava2CallAdapterFactory:
Retrofit retrofit = new Retrofit.Build()
.addCallAdapterFactoryFactory(RxJava2CallAdapterFactory.create())
,build()
那么接口listReops的返回类型可谓Observable< T>。
build最后初始化了转换器工厂类的集合,转换器工厂主要负责网络响应的解析,如果我们设置为GsonConverterFactory,那么就可以使用Gson解析网络结果,如果设置为ProtoConverterFactory,则可以使用ProtoBuf解析网络结果。
2、实现网络接口
在示例中,我们定义了网络接口Githubservice,然后使用创建的Retrofit对象的create方法来实现该接口,网络接口的实现为Retrofit最重要的逻辑,而网络接口的实现就是使用Java的动态代理实现的。create方法的调用和实现如下:
GitHubService githubService = retrofit.create(GitHubService.class);
public <T> T create(final Class<T> 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 {
...
//创建ServiceMthod对象,完成接口方法的解析和封装
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
Retrofit的create方法的主要逻辑就是返回一个GithubService接口的代理对象。注意这里是代理对象,并不是通过implement关键字实现GithubService接口的实现类,当我们调用代理对象的接口方法时,如调用listRepos方法时:
Call<Lsit<Repo>> octocat = githubSerivce.listRepos("octocat");
就会触发InvocationHandler的invoke方法的调用来完成listRepos接口的实现:返回一个Call< T>对象。
在invoke方法中,调用loadServiceMethod加载一个ServiceMethod对象,如果已经加载过了,则会从缓存中获取,如果没有缓存,则会创建一个新的ServiceMethod对象,在创建过程中完成了对网络接口方法的解析,进而封装成一个ServiceMethod对象。ServiceMethod对象中的成员变量如下:
final class ServiceMethod<R,T>{
final okhttp3.Call.Factory callFactory;
final CallAdapter<R,T> callAdapter;
private final HttpUrl baseUrl;
private final Converter<RespinseBody,R> respinserConverter;
private final String httpMethod; //请求方法:POST GET等
private final String relativeUrl;
private final Headers headers; //请求头
private final MediaType contentType;
...
}
这些成员变量大部分都是通过解析网络接口方法的注解完成初始化的。在获取到ServiceMethod对象后,使用它完成OkHttpCall< Object>对象的创建。最后通过代码:
return serviceMethod.callAdapter.adapt(okHttpCall);
返回一个对象。这个对象在当前示例下返回的是一个 ExecutorCallbackCall对象,它的创建过程还是比较复杂的,首先serviceMethod中的callAdapter对象是由createCallAdapter创建,而 createCallAdater内部是使用Retrofit对象来获取一个CallAdapter对象,createCallAdapter的调用和实现如下:
public ServiceMethod build() {
callAdapter = createCallAdapter();
}
private CallAdapter<?> createCallAdapter() {
...
try {
return retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(e, "Unable to create call adapter for %s", returnType);
}
}
...
callAdapter方法会遍历CallAdapter.Factory集合adapterFactories,根据网络接口方法的返回值来匹配CallAdapter对象,进而返回对应的CallAdapter。Retrofit的callAdapter方法的实现如下:
//Retrofit.java
public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = adapterFactories.indexOf(skipPast) + 1;
//遍历CallAdapter.Factory集合
for (int i = start, count = adapterFactories.size(); i < count; i++) {
//根据网络接口的返回类型匹配相对应的CallAdapter.Factroy,进而获取CallAdapter对象
CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
....
throw new IllegalArgumentException(builder.toString());
}
之前在创建Retrofit对象的时候,会默认添加一个ExecutorCallAdapterFactroy对象到CallAdapter.Factory的集合中。
所以在遍历CallAdapter.Factroy集合遍历到ExecutorCallAdapterFactory对象时,就会调用该对象的get方法获取一个CallAdapter对象:
//ExecutorCallAdapterFactory.java
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
// 如果匹配的类型不为Call则返回null
if (rawType != Call.class) {
return null
}
final Type responseType = Utils.getCallResponseType(returnType);
//返回一个CallAdapter对象
return new CallAdapter<Object,Call<?>>{
@Override
public Type responseType(){
return responseType
}
@Override
public Call<Object> adapter(Call<Object> call){
return new ExecutorCallback<>(callbackExecutor,call);
}
}
}
在get方法中会根据网络接口的返回类型进行匹配,如果返回类型不为Call,则返回null,如果为Call类型,比如网络接口方法listRepos的返回类型为Call,那么就会通过判断来返回一个CallAdapter对象。再回到之前的代码:
serviceMethod.callAdapter.adapter(okHttpCall);
所以serviceMethod.callAdapter实际上是获取的是CallAdapter对象,CallAdapter对象的adapter方法会返回一个ExecutorCallbackCall对象。
所以,如果网络接口的返回类型为Call类型,那么在InvocationHandler的invoke方法中返回对象的类型为ExecutorCallbackCall。
3、网络请求的发送
通过网络接口的的实现可知,调用接口方法如下:
Call<List<Repo>> octocat = getHubService.listRepos("octocat");
返回的Call对象实际上是ExecutorCallbackCall对象,所以接下来我们使用Call对象发送网络请求,如异步网络请求:
octocat.enqueue(mCallback);
实际上执行的是ExecutorCallbackCall中的enqueue方法,enqueue方法的实现如下:
@Override
public void enqueue(final Callback<T> callback) {
......
call.enqueue(new okhttp3.Callback() {
//内部使用okhttp完成异步请求
@Override
//网络成功的回调
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
......
});
在enqueue方法内部,最终使用OkHttp执行网络请求,网络响应或失败都会通过MainThreadExecutor分发到主线程。
总结
- 从Retrofit的
build
方法 建造出一个 Retrofit对象,过程中有两个重要的工厂集合:
(1)adapterFactories
集合,是CallAdapter的工厂实现类,里面装着网络回调执行器callbackExecutor(就是绑定主线程的Handler)
(2)converterFactories
集合,Converter的工厂实现类,里面装着网络响应转化器,比如说Gson - 网络接口的实现为Retrofit的最终要的逻辑,也就是其
create
方法
通过动态代理将网络接口转化成一个ServiceMethod
对象,这个ServiceMethod中封装了网络请求的所有信息,然后通过OkHttpCall<>(ServiceMehtod,args)
来封装成一个OkhttpCall对象,最后用这个OkHttpCall通过callAdapter
方法 遍历一开始的adapterFactories集合,通过get
方法最终来返回与网络接口匹配的CallAdapter对象。 - 通过create最终创造的CallAdapter对象可以通过调用其接口方法其实现一个同步/异步请求。
在异步请求中,enqueue
实际上就是调用CallAdapter的enqueue方法 通过OkHttp
实现网络请求,执行的结果通过MainThreadExecutor对象回调到主线程中。
关于OkHttp和Retroift疑问
有人就会问了,为什么Retrofit的网络请求是基于Okhttp,我们要去用Retrofit,而不是直接去用OkHttp呢?
我们先来看看OkHttp的特点:
- 支持SPDY,因此可以同一IP多个连接共享同一个socket(SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强)
- 在Http/2不可用时, 连接池可极大减少延时;
- 支持Gzip压缩响应体,降低传输内容的大小;
- 支持Http缓存,避免重复请求;
- 服务器
- 配置多IP情况下,当前IP请求失败,支持自动切换到其他IP;
。。。。
再来看看OhHttp的缺点:
- 是消息回来需要切到主线程,主线程要自己去写。
- 调用比较复杂,需要自己进行封装。
- 缓存失效:网络请求时一般都会获取手机的一些硬件或网络信息,比如使用的网络环境。同时为了信息传输的安全性,可能还会对请求进行加密。在这些情况下OkHttp的缓存系统就会失效了,导致用户在无网络情况下不能访问缓存。
再来看看Retrofit的优缺点
- 可以配置不同HTTP client来实现网络请求,如okhttp、httpclient等;
- 请求的方法参数注解都可以定制;
- 支持同步、异步和RxJava;
- 超级解耦;
- 可以配置不同的反序列化工具来解析数据,如json、xml等;
- 使用非常方便灵活;
- 框架使用了很多设计模式
缺点为:
- 不能接触序列化实体和响应数据;
- 执行的机制太严格;
- 使用转换器比较低效;
- 只能支持简单自定义参数类型;
再从这些特点做出比较,比较出他们的区别:
- 职责不同
Retrofit主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
而OkHttp主要负责socket部分的优化,比如多路复用,buffer缓存,数据压缩等等。 - 封装不同
Retrofit封装了具体的请求,线程切换以及数据转换。
而Okhttp是基于Http协议封装的一套请求客户端,虽然它也可以开线程,但根本上它更偏向真正的请求,跟HttpClient, HttpUrlConnection的职责是一样的。
另外,网上一般都推荐RxJava+Retrofit+OkHttp框架,Retrofit负责请求的数据和请求的结果,使用接口的方式呈现,OkHttp负责请求的过程,RxJava负责异步,各种线程之间的切换。