带你一步步剖析Retrofit 源码解析:一款基于 OkHttp 实现的网络请求框架

本文深入剖析了Retrofit如何基于OkHttp进行网络请求的封装,包括Retrofit对象构建、Service对象创建、注解解析、HTTP请求处理等关键步骤。Retrofit利用动态代理和注解解析,简化了网络请求接口的使用,同时支持了对协程的整合。文章详细介绍了从创建Retrofit实例到执行网络请求的整个流程,展示了Retrofit优雅地扩展OkHttp功能的设计思想。
摘要由CSDN通过智能技术生成

OkHttp与Retrofit 的区别与联系是怎样的?

参考答案:
OkHttp和Retrofit都是目前流行网络开源框架

封装不同:
Retrofit封装了具体的请求,线程切换以及数据转换。
retrofit通过使用代理,外观,策略模式对okhttp进行了封装
OkHttp 是基于Http协议封装的一套请求客户端

职责不同:
Retrofit主要负责应用层面的封装,面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
OkHttp主要负责socket部分的优化与封装,比如网络访问,多路复用,buffer缓存,数据压缩等等。

在这里插入图片描述

顺手留下GitHub链接,需要获取相关面试等内容的可以自己去找
https://github.com/xiangjiana/Android-MS

更多完整项目下载。未完待续。源码。图文知识后续上传github。
可以点击关于我联系我获取

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

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

基本使用
我们首先来看看 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 的话,会对 Service 中所有非平台独有且非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所包含的注解、参数包含的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值