Retrofit 原理分析 (面试篇)

266 阅读4分钟

Retrofit解决了哪些问题

  1. 简化了用户网络请求的参数配置等
  2. 可以与Rxjava相结合轻松实现线程切换
  3. 自动将返回的Response转换成我们需要的Bean类。

Retrofit用到了哪些设计模式

1. 构建者模式

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient())
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
        .build();

创建Retrofit的方式使用的是构建者模式,对于多参数情况配置,调理更加清晰。

2. 动态代理模式

ICoinRankService service = retrofit.create(ICoinRankService.class);

我们知道在使用 retrofit的过程中,只需要定义一个接口类和带注解的函数,然后调用retrofitcreate方法就可以得到可以进行网络请求的实现类。那么我们没有自己写接口的实现类,retrofit是如何生成的实现类呢?下面看一下create函数的具体代码。

public <T> T create(final Class<T> service) {
 ...
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
      new InvocationHandler() {
      ...

        @Override public @Nullable Object invoke(Object proxy, Method method,
            @Nullable 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);
          }
          //默认情况,java接口是不能有默认实现的,但java8之后接口可以有默认实现,所以需要排除
          if (platform.isDefaultMethod(method)) {
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
      });
}

通过上面代码,我们可以看到调用create函数,返回的是Proxy.newProxyInstance,由此我们知道retrofit是通过动态代理以及反射的机制生成实现类,不过这个实现类是在运行时才会生成,编译器是看不到的。

这里说明一下使用动态代理的好处:使用动态代理可以实现AOP思想,将所有的网络请求逻辑能够进行统一处理,简化了代码逻辑,也就实现了上面说的简化了用户网络请求的参数配置。

3. 适配器模式

适配器模式的定义是将一个类的接口变换为客户端期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作

Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient())
        .baseUrl("https://api.github.com/")
        .addConverterFactory(GsonConverterFactory.create())//设置数据解析器
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加RxJava适配器
        .build();

通过addCallAdapterFactory添加对应的适配器工厂,是为了转换接口请求返回的类型,比如添加RxJava2CallAdapterFactory就可以返回Observable类型可以与rxjava相结合进行线程切换,如果不添加对应的适配器工厂就只能支持默认的返回类型,也可以自定义适配器以适配特定的返回类型。而RxJava2CallAdapterFactory里面会创建RxJava2CallAdapter

public interface CallAdapter<R, T> {}
final class RxJava2CallAdapter<R> implements CallAdapter<R, Object> {}

这里RxJava2CallAdapter实现CallAdapter用的就是适配器模式,CallAdapter设计的目的是将响应类型为{@code R}的{@link Call}修改为{@code T}类型,在具体的实现类RxJava2CallAdapter中它将Call类型转换为Object类型,之所以是Object类型,是因为RxJava支持Flowable、Single、Observable等多种类型,所以取了他们的父类。

4. 抽象工厂模式

抽象工厂模式的定义是为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

abstract class Factory {}
public final class RxJava2CallAdapterFactory extends CallAdapter.Factory {}

其中CallAdapter.Factory为抽象工厂,CallAdapter为工厂的产品接口,不同的CallAdapter.Factory抽象工厂创建不同的CallAdapter产品

Retrofit中的ServiceMethod

通过上面内容我们得知Retrofit主要是在create方法中采用动态代理模式实现接口方法;这个过程构建了一个ServiceMethod对象,根据方法注解获取请求方式、参数类型、参数注解,然后拼接网络请求的链接,这里是通过反射的机制去拿到注解方法具体的请求参数,那么这里就会出现一个性能问题,retrofit做了一个缓存机制以优化性能问题,如下所示:

public final class Retrofit {
  //新建一个map集合用于缓存
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
  
  ServiceMethod<?> loadServiceMethod(Method method) {
  //每次去加载的时候先去map缓存去查询是否有
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
      //每次拿到反射结果的时候都去先存到map集合里面
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
}

原理总结

综上所述,Retrofit核心原理就是通过动态代理,注解和反射机制,以及可定制化的工厂类实现网络请求前的参数配置,以及网络请求之后的数据转换,具体的网络请求是交给okhttp去执行。