Retrofit是一个在Android和Java中类型安全Http客户端框架,可以通过注解的方式声明请求方法、参数和URL路径,此外还支持mutlipart请求和文件上传。Retrofit2.0是基于OkHttp实现的,底层的网络请求由OkHttp完成,下文的分析中会详细提及两者的关联。
首先需要定义一个通用的基础类ServiceGenerator,ServiceGenerator是整个框架的核心,代码如下:
public class ServiceGenerator {
public static final String API_BASE_URL = "http://your.api-base.url";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static S createService(Class serviceClass) {
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
}
在ServiceGenerator中定义了BASE_URL,后续所有的业务请求只需要提供相对路径即可,GsonConverterFactory表示网络请求返回值通过Gson解析,OkHttpClient.Builder可以配置几乎所有的网络请求相关的参数,具体可以参照OkHttp的用法和源码,最后一行代码retrofit.create(serviceClass)很重要,会在下文的源码分析中重点提及。
以请求Github仓库的贡献者列表为例,此时需要先定义一个接口,并且声明具体的业务请求方法,代码如下:
public interface GitHubClient {
@GET("/repos/{owner}/{repo}/contributors")
Call> contributors(
@Path("owner") String owner,
@Path("repo") String repo
);
}
@GET表示网络请求方法为get请求,URL路径中的{ower}{repo}是由具体的调用方通过参数传递的方式提供的,@Path为URL占位符标识,Contributor为贡献者的JavaBean。
接口和请求方法都定义好了,接下来是具体调用和返回值处理,代码如下:
public static void main(String... args) {
// Create a very simple REST adapter which points the GitHub API endpoint.
GitHubClient client = ServiceGenerator.createService(GitHubClient.class);
// Fetch and print a list of the contributors to this library.
Call> call =
client.contributors("fs_opensource", "android-boilerplate");
try {
List contributors = call.execute().body();
} catch (IOException e) {
// handle errors
}
for (Contributor contributor : contributors) {
System.out.println(
contributor.login + " (" + contributor.contributions + ")");
}
}
通过ServiceGenerator生成了GitubClient的对象实例,然后就可以调用之前定义好的网络请求,可以看到返回值只要传入事先定义好的JavaBean,Retrofit就会通过上文配置的Gson进行转换,无需手动解析json,client.contributors方法带有两个参数就是上文中@Path占位符需要的参数,返回值为Call类型的实例对象,这个Call类其实就是OkHttp的静态代理,OkHttp也有Call类,两者接口定义有很多相似之处,可以实现无缝对接。call.execute执行同步请求,线程会一直处于阻塞状态,直到请求成功或失败,返回值可以通过body方法获取。如果是异步请求的话,其他都一样,只是调用Call的enqueue方法,返回值通过回调的方式传递给调用方,代码如下:
call.enqueue(new Callback(){
@Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override public void onResponse(Call call, Response response) throws IOException {
}
});
本文重点不是讲解Retrofit的用法,只是为了方便下面的源码分析。
上文提到了ServiceGenerator最后一行代码return retrofit.create(serviceClass)很重要,也是理解Retrofit最核心的部分,那么就从这里入手来看看create方法究竟做了什么,从这个方法也可以看出参数是我们之前定义好的接口,返回值是接口的实例对象,并且可以直接调用网络请求,源码如下。
public T create(final Class 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);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
可以看到该方法主要利用了Java的动态代理(关于动态代理可以参考Java详解相关的书籍和文档,这里就不过多解释了),InvocationHandle的invoke方法会在service接口每个方法被调用的时候传入invoke方法体中的代码,重点看最后3行代码。
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
这里有一个新出现的类ServiceMethod,这个类包含了网络请求的大部分参数,我们先来看
loadServiceMethod方法,跟踪进去:
ServiceMethod loadServiceMethod(Method method) {
ServiceMethod result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = new ServiceMethod.Builder(this, method).build();
serviceMethodCache.put(method, result);
}
}
return result;
}
我们只看核心代码result = new ServiceMethod.Builder(this, method).build(),这段代码是将网络请求方法method分拆解析,然后在ServiceMethod内部完成处理,跟踪进去:
public ServiceMethod build() {
callAdapter = createCallAdapter();
responseType = callAdapter.responseType();
···
responseConverter = createResponseConverter();
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
···
return new ServiceMethod<>(this);
}
代码比较多,只挑重要的来看,刚才也提到过ServiceMethod的build方法主要是对请求参数进行分拆解析,callAdapter = createCallAdapter()获取返回值的处理方式,Retofit支持目前比较流行的Call Adapter,分别是
具体用法是在Retrofit初始化时通过addCallAdapterFactory()方法配置,如果是RxJava,只需传参RxJavaCallAdapterFactory.create()即可。
createResponseConverter()方法主要是获取返回值Converter,Retrofit初始化时配置addConverterFactory(GsonConverterFactory.create())就表示通过Gson解析返回值。
然后是循环获取Annotation参数并处理,看一下parseMethodAnnotation方法的具体代码:
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
通过解析Annotation参数可以获得请求方法和请求参数,就是在这里分类拼装的,这个方法基本上囊括了Http请求的所有请求方式,除了常见的get,post,put,delete还有header,patch,options,multipart等等,这里主要是获取业务请求的请求方式、url、请求参数。
至此基本上就分析完了Retrofit对request的解析。
回到Retrofit的create方法倒数第二行代码
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
OkHttpCall实现了Retrofit的Call接口,Call接口定义了和OkHttp库的Call接口相同的方法,所以OkHttpCall就是OkHttp的静态代理,请求url和参数经过解析后直接透传至OkHttp的Call接口,可以看一下OkHttpCall的方法列表。
可以看到execte就是执行同步请求,enqueue执行异步请求,还可以取消请求,Retrofit还有一个特性是提供了clone方法,如果你想利用之前的请求方法,可以直接拷贝一份,参数配置完全一样。
回到Retrofit的create方法,最后一行代码
return serviceMethod.callAdapter.adapt(okHttpCall)
之前配置好的CallAdapter开始适配返回值处理方式了,注意到这里网络请求还没有开始执行,获取到的OkHttpCall也只是参数配置完成,真正的网络请求需要手动调用call.execte()或者call.enqueue(),如果是配置了RxJava处理方式,那么网络请求是在subscribe时才执行的。
上文围绕Retrofit的create方法分析了整个网络请求从参数解析到执行网络请求的全过程,牵扯到OkHttp和RxJava相关的处理没有详细展开,因为这两个不是本文的重点,关于OkHttp会在接下来的文章中详细阐述和分析。
Retrofit提供了一种清晰简单的请求定义方式,通过简单的注解即可完成复杂的参数定义,网络请求直接封装了目前Android最流行的OkHttp框架,如果再结合RxJava一起使用,网络请求代码将会变得更优雅清晰,关于Retrofit的详细用法会在下一篇文章中详细阐述,敬请期待。