在Android开发方面网络请求框架层出不穷,由刚开始使用的URLConnection,HttpClient,AsyncHttpClient ,Volley。到现在最流行的OkHttp ,Retrofit, 对一个Android开发者来说再熟悉不过,就其OkHttp和Retrofit可以说的上是黄金组合,Retrofit其实并不负责网络请求,只是完成对api的封装,再交给底层的网络框架去完成(Retrofit2 只能是OkHttp)网络请求, 而且并不局限于Android平台,在Java中都可使用。
在这本文中提到的Retrofit均指Retrofit2
一、Retrofit在网络请求中充当什么角色
既然Retrofit并不负责网络请求,那它又有什么作用呢?看下面的代码
public final class SimpleService {
public static final String API_URL = "https://api.github.com";
public static class Contributor {
public final String login;
public final int contributions;
public Contributor(String login, int contributions) {
this.login = login;
this.contributions = contributions;
}
}
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
public static void main(String... args) throws IOException {
// Create a very simple REST adapter which points the GitHub API.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// Create an instance of our GitHub API interface.
GitHub github = retrofit.create(GitHub.class);//创建GitHub实体
// Create a call instance for looking up Retrofit contributors.
Call<List<Contributor>> call = github.contributors("square", "retrofit");
// Fetch and print a list of the contributors to the library.
List<Contributor> contributors = call.execute().body();
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
}
上边这行代码是请求github的api,查看Retrofit的contributors。可以看到只需要定义一个GitHub的接口,并在接口中写对应方法,并添加Retrofit注解,用于描述接口的参数名称,参数的个数,参数位于http请求的相关位置以及请求路径。就这样一条接口就编写完成。在使用接口时只需要调用github这个对象的方法获得Call并执行就能完成http请求了。
在上面我们只定义了接口并未去编写其实现类,那我们有又要怎么创建一个GitHub实体呢?
这就是Retrofit要做的了,通过方法retrofit.create(GitHub.class)创建了GitHub实现类,该实现类能完成对请求参数的拼接,返回数据的解析并封装成接口对应的数据类型。
可以看到Retrofit提供的功能很单一,只是完成接口的封装。它根据我们定义的接口类生成了实现类,该实现类完成了请求参数的封装和返回数据封装为实体的功能。下面我们就从Retrofit是如何将生产GitHub这个接口的实现类开始分析。
二、定义的接口的实现类创建及Call对象生成流程
Retrofit的create(Class service)方法用于创建接口的实现类,源码如下:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);//校验class是否是单个接口,不通过将抛出IllegalArgumentException
if (validateEagerly) {
eagerlyValidateMethods(service);
}
//通过java动态代理的方式上传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) {//若是Object中的方法直接调用
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {//判断方法是否为Java8接口的default方法,直接调用接口中的方法
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);//该类完成了对请求接口方法注解的参数解析,路径解析等功能
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);//相当于一个okhttp3.Call的代理对象,根据传入的args参数,构建OkHttp的request对象并生成okhttp3.Call对象
return serviceMethod.callAdapter.adapt(okHttpCall);//可以理解为完成了线程的转换,在Android中返回数据有时需要在主线程回调,在java平台上callAdapter.adapt默认什么都没做
}
});
}
我们把有Retrofit生成的对象命名为$Proxy0(其实就是上面GitHub接口的实现类)。在Retrofit接口中定义的方法返回类型都应为Call对象并指定参数类型,$Proxy0对象方法的调用生成call对象的流程流程如下。
有了接口对应的Call对象现在我们就能通过生成的Call对象进行网络调用了。
三、Call对象的方法调用流程
这里的Call其实是Retrofit对OkHttp中Call对象的一个代理,最终是会调用用OkHttp中Call对应的方法。
Call对象中主要调用的方法有:
Response<T> execute() throws IOException;
void enqueue(Callback<T> callback);
第一方法为同步方法,第二个方法为异步方法;在Android环境中enqueue方法的回调对象callback将会在主线程中调用。方法对用流程如下: