Retrofit-原理全解析

本文深入解析Retrofit的工作原理,从创建Retrofit对象、通过建造者模式初始化,到通过动态代理创建对象并发起网络请求。详细阐述了Retrofit如何通过Java动态代理、ServiceMethod和OkHttpCall进行请求的封装与执行,展示了Retrofit在设计模式和面向对象编程上的巧妙运用。

1.Retrofit介绍

Retrofit是一个RESTful的Http网络请求框架的封装,网络请求部分本质是由OKHttp完成的

而我们学习Retrofit除了对Http请求有进一步的了解之外,我们还能通过学习Retrofit 源码感受到面向对象特性运用的极致与合理运用设计模式带来的代码优雅。

本篇内容我是面向已经会用Retrofit的开发者写的,不会再重复介绍Retrofit用法,如果还不会使用Retrofit我建议先去看看官方文档后再来学习本文章,那么事不宜迟,我们开始吧!

2.原理流程

本文将会从源码角度,讲述Retrofit从初始化,到最后请求回调到开发者这段过程究竟发生了什么进行详细分析解说。

2.0 创建Retrofit对象

我们通过了解Retrofit初始化的过程,可以管中窥豹的了解Retrofit所需什么东西,能对Retrofit有一个初步的了解。

2.0.1 通过建造者模式创建Retrofit对象

因为Retrofit初始化时可选参数特别的多,所以Retrofit用到建造者模式来创建对象,我们可以看看源码中Retrofit是怎么实现建造者模式的。

public final class Retrofit {
   
   
  
    //...some members
    
    //1.Retrofit的构造器是外部不可见状态,需要通过Builder创建
    Retrofit(
      okhttp3.Call.Factory callFactory,
      HttpUrl baseUrl,
      List<Converter.Factory> converterFactories,
      List<CallAdapter.Factory> callAdapterFactories,
      @Nullable Executor callbackExecutor,
      boolean validateEagerly) {
   
   
      
    //init something...
    }
 
    //...some functions
    
    //2.Retrifit的Builder
    public static final class Builder {
   
   
      
        //...some members
    
        Builder(Platform platform) {
   
   
          this.platform = platform;
        }
        
        public Builder() {
   
   
          //3.Retrofit自己寻找合适的平台通过,目前有Android与Java,通过获取虚拟机的名称判断是否Android和Java
          this(Platform.get());
        }
    }
    
    //set something...
    
    //4.通过build函数创建Retrofit对象,下面章节再build详细介绍方法
     public Retrofit build() {
   
   
      // do something...
      return new Retrofit(...)
    }
    

通过阅读上面源码,我们得知以下两点

  1. Retrofit对外隐藏了构造器
  2. 提供了Builder对象,调用build()创建Retrofit对象

那么,Builder提供了什么set函数给我们初始化Retrofit呢?我们展开看看

2.0.2 Retrofit.Builder详细介绍

我们直接通过阅读源码来介绍Retrofit.Builder的详情吧~

  public static final class Builder {
   
   
        //介绍常用初始化函数
        //1.设置用于请求Http的客户端,一般为OkHttpClient
        public Builder client(OkHttpClient client) {
   
   
          return callFactory(Objects.requireNonNull(client, "client == null"));
        }   
        //1.1 设置生产OkHttpCall(真正做请求)的工厂
         public Builder callFactory(okhttp3.Call.Factory factory) {
   
   
          this.callFactory = Objects.requireNonNull(factory, "factory == null");
          return this;
       }

        
        /**
        2.设置全局基础域名
        这样我们声明接口的时候,就只需要填入路径即可。
        Retrofit还提供以下重载方法:
        public Builder baseUrl(URL baseUrl)
        public Builder baseUrl(HttpUrl baseUrl) {
        */
        public Builder baseUrl(String baseUrl) {
   
   
          Objects.requireNonNull(baseUrl, "baseUrl == null");
          return baseUrl(HttpUrl.get(baseUrl));
        }
        
        /**
        3.设置数据(请求、响应)转换器工厂
            1.某些请求(Body、PartMap等),需要用到转换器
            2.在接收响应时,也会用到转换器
        在多场景用到不同转换器的时候,在设计模式中肯定使用工厂模式来生产对应的转换器,统一了生产逻辑
        */
        public Builder addConverterFactory(Converter.Factory factory) {
   
   
            converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
            return this;
        }
        
        /**
        4.设置请求适配器工厂,用于生产实际请求时做请求逻辑的对象
        我个人对这个类的理解就是Retrofit给开发者提供了hook请求时候的机会,我们可以稍微通过DefaultCallAdapterFactory得知其会默认返回ExecutorCallbackCall,对真正做请求动作的OkHttpCall进行代理
        所以我们可以通过自定义这个CallFactor生产我们自己自定义过的CallAdapter,在整个流程中植入我们的逻辑,比如做一些缓存工作之类的,或者公共的逻辑(比如判断接口状态,做出相应行为)
        */
        public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
   
   
          callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
          return this;
        }
    
        /**
        5.设置线程调度器,主要用于ExecutorCallbackCall中,请求接口后从子线程切换到主线程用
        */
        public Builder callbackExecutor(Executor executor) {
   
   
          this.callbackExecutor = Objects.requireNonNull(executor, "executor == null");
          return this;
       }
    }

2.0.3 build函数

阅读Build函数是为了了解当我们没有为某些参数赋值时,Retrofit会为这些参数设置怎么样的缺省值,我们可以通过了解缺省值来了解Retrofit会怎么去实现某一块的实现。

public Retrofit build() {
   
   
    //1.设置全局基础域名
      if (baseUrl == null) {
   
   
        throw new IllegalStateException("Base URL required.");
      }
    
    //2.callFactory判空,默认为OkHttpClient
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
   
   
        callFactory = new OkHttpClient();
      }
    //3.线程调度器判空,默认为平台默认实现的调度器,Android的调度器默认用Handle(MainLooper)实现
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
   
   
        callbackExecutor = platform.defaultCallbackExecutor();
      }

    /**
        4.除了添加自定义的请求适配器,在最后还会添加平台默认请求适配器作为保底,Android的默认实现配合了
        线程调度器,在异步调用时,使用线程调度器切换回主线程
    */
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

    /**
        5.除了添加自定义的转换器,会首先插入Retrofit对某些请求类型预置的转换器(如Streaming)
    */
      List<Converter.Factory> converterFactories =
          new ArrayList<>(
              1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
      //添加预置转换器
      converterFactories.add(new BuiltInConverters());
      //添加自定义转换器
      converterFactories.addAll(this.converterFactories);
      //添加平台默认转换器(Android没有默认转换器)
      converterFactories.addAll(platform.defaultConverterFactories());
      //
      return new Retrofit(
          callFactory,
          baseUrl,
          unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories),
          callbackExecutor,
          validateEagerly);
    }

以上就是Retrofit的初始化过程,了解这个过程我们得知了Retrofit初始化所需的参数~以及他们的作用。

接下来就开始进入核心流程,Retrofit是怎么通过接口创建出对象,又是怎么通过这个对象,进行网络请求的。

2.1 Retrofit通过create(T)创建对象做了什么?

public <T> T create(final Class<T> service) {
   
   
    /**
        1.检查接口是否合法
    */
    validateServiceInterface(service);
    /**
        2.通过动态代理创建对象(详情可返回上面看动态代理的介绍)
    */
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {
   
   service},
            new InvocationHandler() {
   
      // 3.通过代理对象调用的函数最后都会调用该回调对象中的invoke函数
              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 {
   
   
                //4.如果该对象是Object,则直接通过反射调用Object自身函数
                if (method.getDeclaringClass() == Object.class) {
   
   
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)//5.判断是否Java8中的默认方法,如果是的话则调用默认方法
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);//7.这个实际上调用了两个函数,我们在2.2章节中详细剖析
              }
            });
  }

总结
我们通过Retrofit的create方法,可以将一个接口生成为一个对象,其中原理就是通过Java的动态代理创建一个代理对象,其中任何的函数调用,都会通过InvocationHandler回调对象中的invoke函数回调,最后经过该对象的合法性检查后,会分别调用loadServiceMethodloadServiceMethod返回对象的invoke方法。
接下来我们就把Retrofit的面纱揭个不剩吧!

2.2 通过动态代理创建的对象调用方法会发生什么事?

上一章解释了Retrofit是怎么创建对象的,但是留下了两个问题,当我们调用Retrofit生成相应对象的函数后,这个网络请求是怎么传出去的?

loadServiceMethod(method).invoke(args)

我们接下来就聚焦这2个函数,看看Retrofit到底做什么?

2.2.1 loadServiceMethod

我们可以通过函数名与参数推断出这个函数的用途,就是加载这个接口方法,我们来看看loadServiceMethod源码

 ServiceMethod<?> loadServiceMethod(Method method) {
   
   
    //1.从缓存中获取一个ServiceMethod对象
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
   
   
      result = serviceMethodCache.get(method);
      //2.如果缓存中没有,则解析该方法,然后放进缓存
      if (result == null) {
   
   
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

我们可以看出该函数只做两件事:

  1. 从缓存中获取serviceMethod
  2. 如果缓存中没有,则通过ServiceMethod.parseAnnotations解析方法,并且放入缓存中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值