Retrofit 源码解析:一款基于 OkHttp 的网络请求库

本文深入解析了Retrofit网络请求库的源码,介绍了其如何基于OkHttp进行功能扩展,包括注解解析、动态代理创建Service对象、CallAdapter、Converter、ServiceMethod等关键组件的工作原理,展示了Retrofit如何优雅地封装网络请求操作。
摘要由CSDN通过智能技术生成

Retrofit 可以说和 OkHttp 是亲兄弟了,它们都是由 Square 公司推出的网络请求库,并且 Retrofit 实际上是基于 OkHttp 实现的,它在 OkHttp 现有功能的基础上进行了封装,支持通过注解进行网络请求参数的配置,同时对数据返回后的解析、序列化进行了统一的包装,甚至在近期引入了对协程对支持。

今天就让我们一起来看看 Retrofit 是如何在 OkHttp 这样一个已经固定的框架的基础上,优雅的进行封装并拓展功能的。

本篇源码解析基于 Retrofit v2.7.0
个人博客:https://blog.N0tExpectErr0r.cn
小专栏:https://xiaozhuanlan.com/N0tExpectErr0r

基本使用

我们首先来看看 Retrofit 的基本使用,来对它有个大致的了解。

首先,我们可以构建一个如下的请求 Service 类,它里面对各个请求的方法及参数通过注解进行了标注:

public interface GitHubService {
   
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

之后,我们可以构建一个 Retrofit 对象,并通过 Retrofit.create 方法传入对应 class 从而构建对应的 Service 对象:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

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

之后,我们调用 service 中对应的方法,就可以获取到 Call 对象了。

通过对 Call 对象调用 enqueue 就可以实现对请求的异步调用,而通过 execute 方法则可以实现请求的同步调用。

Retrofit 对象的构建

Retrofit 采用了 Builder 模式进行了构建,在 Builder 中可以进行非常多的配置,其中可以对 baseUrlokhttpClientconverterFactorycallAdapterFactory 等进行设置。

这里没什么特别的,都是一些简单的赋值,就不再关注了,我们只需要看看最后 Retrofit 被传入了哪些参数。它最后调用了下面这个构造函数对参数进行了初始化。

Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
    List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
    @Nullable Executor callbackExecutor, boolean validateEagerly) {
   
  this.callFactory = callFactory;
  this.baseUrl = baseUrl;
  this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
  this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
  this.callbackExecutor = callbackExecutor;
  this.validateEagerly = validateEagerly;
}

Service 对象的创建

动态代理创建 Service 代理类

接着我们看到自己定义的 interface 是如何仅仅靠传递 classRetrofit.create 就能实现实例的获取的,它明明只是个接口呀?

public <T> T create(final Class<T> service) {
   
	// 对 Service 的接口进行检测
  validateServiceInterface(service);
  // 通过动态代理构建代理对象
  return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] {
    service },
      new InvocationHandler() {
   
        private final Platform platform = Platform.get();
        private final Object[] emptyArgs = new Object[0];
        @Override public @Nullable Object invoke(Object proxy, Method method,
            @Nullable Object[] args) throws Throwable {
   
          // Object 类的方法照常调用
          if (method.getDeclaringClass() == Object.class) {
   
            return method.invoke(this, args);
          }
          // 如果是对应平台本身的类就有的方法,照常调用
          if (platform.isDefaultMethod(method)) {
   
            return platform.invokeDefaultMethod(method, service, proxy, args);
          }
          // 否则通过 loadServiceMethod 方法获取到对应 Method 并 invoke
          return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
        }
      });
}

可以看到,实际上 Service 对象的获取是通过动态代理实现的。这里首先通过 validateServiceInterface 方法对接口进行了检测,之后通过动态代理对该接口进行了代理。

对于 Object 类本身独有以及对应平台本身就存在的方法,就照常调用,否则通过 loadServiceMethodService 中对应的 Method 对象进行处理,之后对其调用 invoke 方法。

这里说明了 Retrofit 不是在创建 Service 接口对应对象时就立即对所有该接口中的所有方法都进行注解的解析,而是采用了在方法被调用时才进行注解的解析这种懒加载的思想

接着我们看看 validateServiceInterface 方法:

private void validateServiceInterface(Class<?> service) {
   
	// 判断是否是接口
  if (!service.isInterface()) {
   
    throw new IllegalArgumentException("API declarations must be interfaces.");
  }
  // 判断该接口及其继承的所有接口是否包含了范型参数,如果包含则抛出异常
  Deque<Class<?>> check = new ArrayDeque<>(1);
  check.add(service);
  while (!check.isEmpty()) {
   
    Class<?> candidate = check.removeFirst();
    if (candidate.getTypeParameters().length != 0) {
   
      StringBuilder message = new StringBuilder("Type parameters are unsupported on ")
          .append(candidate.getName());
      if (candidate != service) {
   
        message.append(" which is an interface of ")
            .append(service.getName());
      }
      throw new IllegalArgumentException(message.toString());
    }
    Collections.addAll(check, candidate.getInterfaces());
  }
  // 如果在创建Retrofit时设置了很急切地对Service的方法进行处理,则对非平台独有且非static的方法通过 loadServiceMethod 方法进行处理。
  if (validateEagerly) {
   
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
   
      if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
   
        loadServiceMethod(method);
      }
    }
  }
}

首先,这个方法对 service 进行了检测,保证了它是一个接口并且它和它继承的类中没有范型参数。

之后如果在 Retrofit 创建时设置 validateEagerly 为 true 的话,会对非平台独有且非static的方法通过 loadServiceMethod 方法提前进行处理。

Service 中方法的解析

那么我们来看看 loadServiceMethod 究竟做了些什么:

ServiceMethod<?> loadServiceMethod(Method method) {
   
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;
  synchronized (serviceMethodCache) {
   
    result = serviceMethodCache.get(method);
    if (result == null) {
   
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

首先它会采用 Double Check 的方式尝试从 serviceMethodCache 缓存中获取 ServiceMethod 对象,如果获取不到则通过 ServiceMethod.parseAnnotations 方法对该 Method 的注解进行处理并将得到的 ServiceMethod 对象加入了缓存。

也就是说为了避免多次对方法的注解进行处理,Retrofit 采用了一个 serviceMethodCache 对解析后的 ServiceMethod 进行缓存

接着我们就来看看,parseAnnotations 方法是如何对方法的注解进行解析的。

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
   
  RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
   
    throw methodError(method,
        "Method return type must not include a type variable or wildcard: %s", returnType);
  }
  if (returnType == void.class) {
   
    throw methodError(method, "Service methods cannot return void.");
  }
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

这里先通过 RequestFactory.parseAnnotations 方法对注解解析并获得了一个 RequestFactory 对象。

之后又通过 HttpServiceMethod.parseAnnotations 方法传入了 requestFactory 继续进行注解的解析并获得 ServiceMethod 对象。

注解解析

我们先看看 RequestFactory.parseAnnotations

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
   
  return new Builder(retrofit, method).build();
}

它把 Method 传入 Builder 从而构建了一个新的 RequestFactory

Builder(Retrofit retrofit, Method method) {
   
  this.retrofit = retrofit;
  this.method = method;
  this.methodAnnotations = method.getAnnotations();
  this.parameterTypes = method.getGenericParameterTypes();
  this.parameterAnnotationsArray = method.getParameterAnnotations();
}

Builder 中通过反射获取到了 method 所包含的注解、参数包含的范型以及参数的注解。

接着看看 build 方法:

RequestFactory build() {
   
  for (Annotation annotation : methodAnnotations) {
   
  	// 遍历方法注解对每个注解进行解析
    parseMethodAnnotation(annotation);
  }
  // ...异常的处理
  
  // 对参数进行解析
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
   
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  }
  
  // ... 异常的处理
  return new RequestFactory(this);
}

build 方法中主要是对方法的每个注解调用了 parseMethodAnnotation 进行了解析,并且对每个参数调用了 parseParamter 方法解析为了 ParamterHandler 对象。

parseMethodAnnotation 的代码如下:

private void parseMethodAnnotation(Annotation annotation) {
   
  if (annotation instanceof DELETE
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值