Retrofit2 原理解析

  在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对象的流程流程如下。

\$Proxy0的方法调用流程

有了接口对应的Call对象现在我们就能通过生成的Call对象进行网络调用了。

扫描二维码关注公众号,回复: 1874094 查看本文章

三、Call对象的方法调用流程

这里的Call其实是Retrofit对OkHttp中Call对象的一个代理,最终是会调用用OkHttp中Call对应的方法。
Call对象中主要调用的方法有:

Response<T> execute() throws IOException;
void enqueue(Callback<T> callback);

第一方法为同步方法,第二个方法为异步方法;在Android环境中enqueue方法的回调对象callback将会在主线程中调用。方法对用流程如下:
Call方法调用流程

猜你喜欢

转载自blog.csdn.net/u011928958/article/details/77529855