Retrofit源码分析

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);

1. ServiceMethod

这里有一个新出现的类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,分别是

  • RxJava Observable & Single - com.squareup.retrofit2:adapter-rxjava
  • Guava ListenableFuture - com.squareup.retrofit2:adapter-guava
  • Java 8 CompleteableFuture - com.squareup.retrofit2:adapter-java8

具体用法是在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的解析。

2. OkHttpCall

回到Retrofit的create方法倒数第二行代码

OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);

OkHttpCall实现了Retrofit的Call接口,Call接口定义了和OkHttp库的Call接口相同的方法,所以OkHttpCall就是OkHttp的静态代理,请求url和参数经过解析后直接透传至OkHttp的Call接口,可以看一下OkHttpCall的方法列表。

可以看到execte就是执行同步请求,enqueue执行异步请求,还可以取消请求,Retrofit还有一个特性是提供了clone方法,如果你想利用之前的请求方法,可以直接拷贝一份,参数配置完全一样。

3. CallAdapter

回到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的详细用法会在下一篇文章中详细阐述,敬请期待。

你可能感兴趣的:(Android,Retrofit)