Retrofit流程及设计模式全解析

Posted open-Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Retrofit流程及设计模式全解析相关的知识,希望对你有一定的参考价值。

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载

文章目录

本文基于Retrofit 2.9.0版本源码分析,根据Retrofit源码窥探请求流程及框架设计中使用到的设计模式

1. 代理模式

在文章开始之前先介绍一下代理模式,因为这是Retrofit的入口,其他设计模式参考文章末尾的概括

代理模式:为对象提供一种代理以控制这个对象的访问。某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

组成:

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
  • 真实角色(委托类):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
  • 代理角色(代理类):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
//抽象
interface IFlyable
    void fly() throws Exception;

//真实类
class Bird implements IFlyable 
    @Override
    public void fly() throws Exception 
        Thread.sleep(1000);
        System.out.println("鸟挥动翅膀起飞了...");
    

//代理类
class FlyTimeProxy implements IFlyable 
    private IFlyable flyable;
    public FlyTimeProxy(IFlyable flyable) 
        this.flyable = flyable;
    
    @Override
    public void fly() throws Exception 
        long start = System.currentTimeMillis();
        flyable.fly();   //代理类调用真实类
        long end = System.currentTimeMillis();
        System.out.println("方法执行时长:" + (end - start));
    


class Test 
    public static void main(String[] args) throws Exception 
        //通过代理类间接访问委托类
        new FlyTimeProxy(new Bird()).fly();
    

代理模式分为静态代理和动态代理

1.1 静态代理模式

上面示例代码中,通过FlyTimeProxy代理来控制IFlyable的目标子类对象,为fly方法增加了时间统计。在编写代码的时候代理类方法中直接调用了委托类,这就属于静态代理。静态代理是由程序员创建或工具生成代理类,是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

缺点:

  • 代理类和委托类实现了相同的接口,出现了大量的代码重复。
  • 代理对象只能服务于特定的委托对象,如果需要代理其他类,则要为其他类创建代理类。

比如FlyTimeProxy现在只能代理IFlyable的子类对象为fly方法计算执行时间,现在我需要为IRunnable会跑的run()也计算时间,就只能创建一个RunTimeProxy代理对象了。

有没有办法我只创建一个TimeProxy就可以代理所有类,为其方法计算执行时间呢?

1.2 动态代理模式

动态代理是在实现阶段不用关心代理类,而在运行阶段才动态创建出代理对象,它是基于反射实现的。Spring的两大思想IoC(依赖注入控制反转)和AOP(面向切面编程:在不改变源码的情况下,在方法中插入自定义逻辑)中的AOP就是基于动态代理机制实现的。

JDK提供了java.lang.reflect.InvocationHandlerjava.lang.reflect.Proxy类来实现动态代理机制,Proxy是自动生成代理类的父类,它维护了InvocationHandler的实例对象,并提供了根据委托类自动创建代理对象的方法Proxy.newProxyInstance()。当调用代理对象(Proxy的子类对象)的方法时实际上是调用InvocationHandler.invoke()方法,从而控制委托类的调用

//抽象
interface IFlyable
    void fly() throws Exception;

//真实类
class Bird implements IFlyable 
    @Override
    public void fly() throws Exception 
        Thread.sleep(1000);
        System.out.println("鸟挥动翅膀起飞了...");
    

/**InvocationHandler:提供了代理对象调用委托对象的功能*/
class TimeHandler implements InvocationHandler
    private Object target;  // 目标对象

    public Object createProxyInstance(Object target)
        this.target=target;
        /**
         * Proxy:提供用于创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
         * 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
         * 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
         * 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    

    /**
     * 当调用代理类方法时,将会执行此方法
     * @param proxy 代理对象
     * @param method 目标对象的方法
     * @param args 方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        //在调用目标类方法前后插入逻辑
        long start = System.currentTimeMillis();
        method.invoke(target, args);   //调用真实类
        long end = System.currentTimeMillis();
        System.out.println("方法执行时长:" + (end - start));
        return null;
    


class Test 
    public static void main(String[] args) throws Exception 
        //通过代理类间接访问委托类
        IFlyable proxy = (IFlyable) new TimeHandler().createProxyInstance(new Bird());
        proxy.fly();
    

1.3 动态代理的原理

动态代理的原理是为委托类自动创建代理类,并通过反射获取代理类的实例。当调用代理类对象方法时,实际上时调用了InvocationHandler.invoke()从而实现拦截。如下自动生成的代理类简约代码:

public final class $Proxy0 extends Proxy implements IFlyable
	//代理类Proxy维护了InvocationHandler实例
    public $Proxy0(InvocationHandler invocationhandler)
        super(invocationhandler);
    
    public final void fly(String s, String s1)
    	//调用代理类的方法实际上是调用InvocationHandler.invoke()从而实现拦截
        super.h.invoke(this, m3, new Object[] 
            s, s1
        ); 
    
    private static Method m3;
    static 
        m3 = Class.forName("IFlyable").getMethod("fly", new Class[] );

    

2. Retrofit接口代理对象的创建

通过retrofit.create(ApiService::class.java)获取接口的实例对象,这个对象是一个动态代理对象,当调用代理对象的方法时,会被拦截得到方法、参数,再调用loadServiceMethod()解析接口上的注解,封装成一个HttpServiceMethod对象并调用其invoke()执行Okhttp请求

/**Retrofit.java*/
public <T> T create(final Class<T> 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];
                        //调用接口代理对象方法实际上是调用了InvocationHandler.invoke()
                        @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);
                            
                            args = args != null ? args : emptyArgs;
                            //如果是默认方法(如java8)就执行 platform 的默认方法
                            return platform.isDefaultMethod(method)
                                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                                    /**
                                     * ★★★调用HttpServiceMethod.invoke()为Retrofit接口返回具体的请求类型对象,比如接口中定义的Call、Observable等
                                     * 该语句主要做了2件事:
                                     * ①. Retrofit的Call创建
                                     * ②. 通过接口适配器CallAdapterFactory将Call对象转换成其他对象(RxJava方式、挂起函数等)
                                     */
                                    : loadServiceMethod(method).invoke(args);
                        
                    );

3. Retorfit的Call创建

通过Retrofit注解定义的服务器接口方法类我们称为接口,看源码之前,我们要弄清涉及到的主要类以及他们的作用:

  • ServiceMethod:这个抽象类代表一个接口方法对象,它主要提供了通过RequestFactory解析方法上注解的功能,然后交给它的子类HttpServiceMethod处理,并定义了invoke方法,用于返回接口方法的值
  • RequestFactory:这个类用于解析接口方法的注解,并提供了create(Object[] args)方法根据注解配置创建出okhttp3.Request对象,Request代表Okhttp的一个请求
  • HttpServiceMethod:它是ServiceMethod的抽象子类,它将接口方法调用适配为一个Http调用。它维护了requestFactory,具备将接口方法转换为Request对象的能力,并且实现了invoke方法用于创建了Retrofit的Call对象,并通过接口适配器将Call转换成接口方法对应的返回类型

/**Retrofit.java*/
//接口可能被请求多次,缓存提升性能
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) 
    //从缓存中获取接口方法对应的ServiceMethod对象,
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    synchronized (serviceMethodCache) 
        result = serviceMethodCache.get(method);
        if (result == null) 
            //1 解析接口方法上的注解,创建ServiceMethod对象
            result = ServiceMethod.parseAnnotations(this, method);
            serviceMethodCache.put(method, result);  //缓存
        
    
    return result;


/**ServiceMethod.java*/
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) 
	//2 通过RequestFactory解析接口上的注解
	RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
	...
    //3 获取一个HttpServiceMethod的子类对象
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);


/**HttpServiceMethod将接口方法的调用适配为一个Http调用*/
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> 
	//根据接口方法配置创建一个HttpServiceMethod子类对象
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
            Retrofit retrofit, Method method, RequestFactory requestFactory) 
        ...
        //接口适配器
        CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
		//4 返回一个HttpServiceMethod的子类对象,并将接口适配器传入构造方法
	    if (!isKotlinSuspendFunction) 
	      return new CallAdapted<>(..., callAdapter);
	     else if (continuationWantsResponse) 
	      return (HttpServiceMethod<ResponseT, ReturnT>)
	          new SuspendForResponse<>(...,
	              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
	    
	    ...
    
    @Override
    final ReturnT invoke(Object[] args) 
        //5 ★★★创建一个Retrofit的Call对象,表示一个网络请求任务
        Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
        //6 ★★★adapt的目的是将Call转换成其他对象类型,比如RxJava的Observable
        return adapt(call, args);
    

当调用接口代理对象方法时,实际上是调用到了loadServiceMethod(method).invoke(args)loadServiceMethod(method)的作用是根据接口方法的定义创建出不同的HttpServiceMethod子类对象,然后调用该对象的invoke()方法,invoke中创建了Call对象(OkHttpCall的实例)。

然后通过adapt()方法将Call对象转换成接口真正的返回类型。比如下面定义的两个接口,调用login()时,adapt将返回Call对象,调用login1()时它将返回一个Observable对象,adapt()是通过调用接口适配器对象CallAdapter.adapt(call)来对Call对象进行转换的。

ApiService接口
    |--fun login():Call<User>          //Call
    |--fun login1(): Observable<User>  //RxJava

4. CallAdapter接口适配器

在定义Retrofit服务器接口时,可以支持返回Call、Observable等等,如下:


ApiService接口
    |--fun login():Call<User>          //Call
    |--suspend fun login1(): User      //suspend协程挂起函数,稍后单独讲
    |--fun login2(): Observable<User>  //RxJava

 private val retrofit = Retrofit.Builder()
 	...
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())   //添加对RxJava的支持
    .build()

Retrofit可以根据接口方法返回类型自动适配返回对应的对象,这都是接口适配器的作用。接下来我们看看接口适配器有哪些?他们是怎么工作的?

4.1 CallAdapter & Factory

/**将R适配为T*/
public interface CallAdapter<R, T> 
    //Retrofit接口方法返回类型的泛型参数类型,比如方法返回类型<User>,此处将返回User
    Type responseType();
    //将R转换为T返回
    T adapt(<R> );
    abstract class Factory 
        /**根据Retrofit接口方法的返回类型创建一个适配器实例对象 */
        public abstract @Nullable Adapter<?, ?> get(
                Type returnType, Annotation[] annotations, Retrofit retrofit);
        ...
    

CallAdapter接口代表接口适配器,它有一个内部抽象工厂类Factory适配器工厂CallAdapter.Factory的作用是提供了get()方法判断匹配创建适配器CallAdapter对象,而适配器提供了adapt()OkHttpCall类型的call对象转换成接口方法返回值对应类型对象。

构建Retrofit时可添加很多种接口适配器工厂以适配接口方法的不同返回类型,这其中就包括平台默认的接口适配器和通过addAdapterFactory()额外添加的,这些适配器工厂都被保存在callAdapterFactories集合中,这就存在两个问题:

  • 怎样从集合中根据接口方法匹配到合适的适配器对象?
  • 适配器是怎么完成call转换的?
public final class Retrofit 
    //接口适配器工厂集合,构造方法中被赋值
    final List<CallAdapter.Factory> callAdapterFactories;   
    
    //Retrofit建造者
    public static final class Builder 
    private final Platform platform;  //代表当前平台,比如android
    Builder(Platform platform) this.platform = platform;
    //添加额外的接口适配器工厂支持
    public Builder addAdapterFactory(Adapter.Factory factory)    
        AdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
        return this;
    
    public Builder()  this(Platform.get());
	    public Retrofit build() 
	        ...
	        //★★★接口适配器工厂集合,首先包含了通过addCallAdapterFactory添加的接口适配器工厂
	        List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
	        //★★★添加平台默认的接口适配器,platform代表平台,比如Android平台默认的接口适配器是DefaultCallAdapterFactory,如果是使用Java8则还会有CompletableFutureCallAdapterFactory(这个适配器将支持kotlin挂起函数)
	        callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
	        ...
	        //创建Retrofit对象,并将接口适配器通过构造方法传入
	        return new Retrofit(...,unmodifiableList(callAdapterFactories),...);
	    
	

	/**★★★根据接口返回类型获取与之匹配的适配器对象*/
    public CallAdapter<?, ?> nextCallAdapter(
            @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) 
        int start = callAdapterFactories.indexOf(skipPast) + 1;
        for (int i = start, count = callAdapterFactories.size(); i < count; i++) 
        	//遍历适配器工厂集合
            CallAdapter<?, ?> adapter = callAdapterFactories.get(i)  
            //根据接口返回类型returnType判断是否符合要求,如果不符合返回null,符合则创建一个接口适配器对象
            		.get(returnType, annotations, this);    
            if (adapter != null)   //直到匹配到合适的接口适配器,并返回
                return adapter;
        
        ...
    
	...

HttpServiceMethod的invoke()方法首先创建了一个OkHttpCall类型的call对象,然后调用adapt(call, args)将call转换成接口返回类型。adapt()方法实际上是通过调用HttpServiceMethod的子类维护的callAdapter接口适配器对象的callAdapter.adapt(call)方法完成的。

HttpServiceMethod.parseAnnotations()方法中通过createCallAdapter()为接口方法创建了一个接口适配器CallAdapter对象并传给其子类对象,跟踪代码发现最后调用到Retrofit的nextCallAdapter()方法去获取适配器对象。该方法将遍历callAdapterFactories集合,调用CallAdapter.Factory.get()方法来获取适配器对象,如果接口返回类型returnType和适配器正好匹配上则创建一个适配器对象,否则返回空继续遍历直到匹配到合适的接口适配器,这就回答了上述第一个问题。

关于call是怎么转换的,接下来我们通过查看几个具体的接口适配器就明白了。

4.2 平台默认的DefaultCallAdapterFactory

Retrofit默认提供对Call的支持,因为Retrofit.build()时添加了Android平台默认的接口适配工厂DefaultCallAdapterFactory,该工厂会判断接口方法的返回类型是否是Call类型,如果是则创建默认接口适

以上是关于Retrofit流程及设计模式全解析的主要内容,如果未能解决你的问题,请参考以下文章

Retrofit源码设计模式解析(下)

Retrofit Gson解析空字符串的问题

Retrofit源码设计模式解析(上)

站在巨人的肩膀上 -- Retrofit源码解析

站在巨人的肩膀上 -- Retrofit源码解析

转转交易全链路接口测试发展及扩展