Retrofit初探和源码解析
前言
在使用了一段时间的Retrofit之后,今天终于在这里讲解到了网络的部分。目前开源的HTTP 框架有很多,Volley,Android Async Http,以及OkHttp +Retrofit等。而我在自己的使用中选择了Retrofit,这里就从基础到原理,再到实例的方式,讲解我对Retrofit做出的一些封装和使用。来让你进一步的了解和掌握Retrofit的使用。
基础
Retrofit一个基于OkHttp的RESTFUL API请求工具。它是 Square 推出的 HTTP 框架,主要用于 Android 和 Java。Retrofit 将网络请求变成方法的调用,使用起来非常简洁方便。
A type-safe HTTP client for Android and Java
如果你还对Retrofit不了解,那么我建议你去官方文档了解一下。
Retrofit使用大体分为三个步骤
(1)Retrofit将HTTP API 转化成了Java接口的形式,所以首先我们会提供一个接口类GitHubService 。
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
(2)Retrofit类可以针对之前定义的GitHubService 接口生成一个具体实现。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
(3)然后就可以对GitHubService 的方法进行同步或者异步的调用来进行网络的访问,也就是说可以通过call对象获得数据:(可以使用enqueue 或者 execute来执行发起请求,enqueue是是异步执行,而 execute是同步执行。)
Call<List<Repo>> repos = service.listRepos("octocat");
通过上面三个步骤,我们会发现Retrofit给人眼前一亮的当然是它的注解调用和优雅的API转化为方法。每一个方法都会对应着一个Http的注解,总共有GET, POST, PUT, DELETE,HEAD五个内嵌的注解。我们也会在注解上指定相应的相对地址信息。比如上方的@GET("users/{user}/repos")
这里本来想将官网所有内容翻译一遍的,返现很多词不达意 。然后今天又凑巧看到了郭神公众号推荐的一篇文章 Android网络请求库 - Say hello to retrofit.对官网对的内容讲解得非常的详细易懂,继续阅读下面章节之前,一定要去看看这篇文章。
于是我们完整的Retrofit使用流程:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
//.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
}
});
Retrofit原理解析
在进一步了解和使用Retrofit之前,不妨先来了解Retrofit的原理,看看Retrofit的源码是了解原理的一个有效途径。
(1) 源码结构
Retrofit包含一个http包,里面全部是定义HTTP请求的Java注解,比如GET、POST、PUT、DELETE、Headers、Path、Query等等。
余下的retrofit包中几个类和接口就是全部retrofit的代码了,代码很少因为retrofit把网络请求这部分功能全部交给了OkHttp了。
(2) 整体流程
继续回到官网的例子。
首先关注的是我们通过new Retrofit.Builder()...build()
进行Retrofit的构建,可以了解的是这里使用的是 Builder 模式。
在Android源码中,经常用到Builder模式的可能就是AlerDialog 了。Builder模式用于将一个复杂的对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。这里Retrofit使用Builder模式支持了支持不同的转换(就是将HTTP返回的数据解析成Java对象,主要有Xml、Gson等)和返回(主要作用就是将Call对象转换成另一个对象,比如RxJava)。这里也就真正的达到了构建复杂对象和它的部件进行解耦。
这里通过build方法来了解Retrofit创建,需要6个参数。如下方代码注解:
public Retrofit build() {
//1 baseUrl 基地址
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
//2 callFactory 默认创建一个 OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
//3 callbackExecutor Android 中返回的是 MainThreadExecutor
callbackExecutor = platform.defaultCallbackExecutor();
}
//4 adapterFactories(比如RxJavaCallAdapterFactory 用于将Call返回支持Rxjava) 把Call对象转换成其它类型
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
//5 converterFactories(例如GsonConverterFactory 用于Gson转换) 请求网络得到的response的转换器的集合 默认会加入 BuiltInConverters ,
List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
//6 private boolean validateEagerly; validateEagerly 是否需要立即解析接口中的方法
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}
所以我们会看到我们通过Builder模式创建Retrofit访问对象都必须指定基地址url。如果还需要支持Gson转换,我们就需要添加.addConverterFactory(GsonConverterFactory.create())
,如果需要支持Rxjava,那么还需要添加 .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
。
接着我们通过GitHubService service = retrofit.create(GitHubService.class);
create方法创建网络请求接口类GitHubService 的实例。我们正是使用该对象的listRepos方法完成了Call<List<Repo>> repos = service.listRepos("octocat");
获取到了数据。下面看看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);
}
//为了兼容 Java8 平台,Android 中不会执行
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
create方法接受一个 Class 对象,也就是我们编写的接口,里面含有通过注解标识的请求网络的方法。注意 return语句部分,这里调用了 Proxy.newProxyInstance方法,这个很重要,因为用了动态代理模式。关于动态代理模式,可以参考这篇文章:公共技术点之 Java 动态代理。简单的描述就是,Proxy.newProxyInstance根据传进来的 Class 对象生成了一个实例 A,也就是代理类。每当这个代理类 A 执行某个方法时,总是会调用 InvocationHandler(Proxy.newProxyInstance中的第三个参数) 的invoke方法,在这个方法中可以执行一些操作(这里是解析方法的注解参数等),通过这个方法真正的执行我们编写的接口中的网络请求。
也就是概括一句话:通过动态代理的方式把 Java 接口中的解析为响应的网络请求,然后交给 OkHttp 去执行。并且可以适配不同的 CallAdapter
,可以方便与 RxJava 结合使用。
更多源码细节,参考这篇文章。Android Retrofit源码解析