细细品读Retrofit的设计之美一

Retrofit是这两年比较流行的网络通信库,之因此流行天然有它的优势,首先是大厂Square公司开源之做,俗话说大厂出品,必须精品。
做为网络通信框架,基本的组成部分就是三大块:1. 请求体部分。2. 响应体部分。3. 与UI层回调部分。java


Retrofit的使用这里就不细说了,咱们从构建Retrofit对象谈起。程序员

private Retrofit buildRetrofit(){
        return new Retrofit.Builder()
                .baseUrl(AppConst.BASE_URL)      // 设置基础请求地址
                .client(buildHttpClient())       // 构建自定义的httpClient 对象
                .addConverterFactory(FastJsonConverterFactory.create())  // 添加数据解析工厂
                .build();  // 构建
    }复制代码

首先:构建者Builder模式

总体看很明显有个构建者Builder模式,Builder模式在Android中是很常见的一种设计模式,它的优势很明显通常状况一个框架都是须要灵活自由的配置参数属性的,若是不用Builder模式,都改为setter、getter,那初始化一个Retrofit对象就显得复杂和臃肿了。而这里Builder模式加上链式调用方式,为Retrofit框架的参数配置增添了很多灵活和自由,并且代码可读性也加强了。
其实Builder模式的套路很简单,下面来个简单的伪代码Builder模式:设计模式

// Builder模式的套路模板
public class Retrofit{
    final HttpUrl baseUrl;
    final List<Converter.Factory> converterFactories;
    ....// 省略一大坨代码

    Retrofit(HttpUrl baseUrl, List<Converter.Factory> converterFactories) {
        this.baseUrl = baseUrl;
        this.converterFactories = unmodifiableList(converterFactories); 
    }
    ....// 省略一大坨代码, 其实就是上面参数属性的一些获取方法
    public HttpUrl baseUrl() {
      return baseUrl;
    }

    public static final class Builder{
        private HttpUrl baseUrl;
        private final List<Converter.Factory> converterFactories = new ArrayList<>();

        public Builder baseUrl(HttpUrl baseUrl) {
          // ... 省去部分代码
          this.baseUrl = baseUrl;
          return this;
        }

        public Builder addConverterFactory(Converter.Factory factory) {
          converterFactories.add(checkNotNull(factory, "factory == null"));
          return this;
        }

        public Retrofit build() {
          // Make a defensive copy of the converters.
          List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

          return new Retrofit(baseUrl, converterFactories);
        }
    }
}复制代码

以上就是Builder模式的套路模板,外部Retrofit的对象的构建最终是在build()方法new出来返回。
Retrofit框架内部有好多地方都用到了Builder模式,也是为了方便自由配置参数的。
Builder模式在Android开发中最多见的就是AlertDialog.Builder,能够自由的配置对话框的标题、内容、内容设置来源、确认取消等按钮事件等等。有兴趣的能够去了解下AlertDialog的源码,基本也是上面模板的套路。api

代理模式

构建好Retrofit对象后,你们都知道这个框架网络请求的通信接口api都是Interface接口中声明的,框架自己为了与网络请求业务作解耦用了动态代理的方式,为业务通信接口生成代理对象,当代理对象调用业务接口方法api的时候,动态代理类就能监测到并回调,这时候就能够作网络框架该有的功能:解析通信业务接口,生成网络请求体便于供应给底层OkHttp作具体的网络请求工做。其实就是框架自己没有办法直接使用业务接口,因此请了一个代理中介对象去间接的访问业务接口,来完成相关的业务功能。数组

public ApiWrapper() {
        // 构建生成retrofit对象
        Retrofit retrofit = buildRetrofit();
        // ApiService是网络经过create方法建立出的代理对象mApiService
        mApiService = retrofit.create(ApiService.class);
}复制代码

来看看create方法是如何建立出ApiService接口的对象的。网络

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

          @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           }
    }
}复制代码

经过Proxy.newProxyInstance方法动态建立了代理对象,也就是上文create方法返回的mApiService对象。是否是很懵逼,为何Proxy.newProxyInstance方法就能建立出代理对象,并且又正好是ApiService.class这个接口对象呢,是怎么建立出来的?带着这些问题,咱们来聊聊“代理模式”。app

要搞清楚上面的这些问题,就得明白代理模式的套路,最重要就是区分清楚角色:
角色一:目标接口
角色二:目标对象
角色三:代理对象框架

这里先举个简单的例子,有个这样的场景:ide

Jerry是个程序员年纪大了眼看就要30岁了仍是单身,家里人很是的着急,因而找到了隔壁老王(老王认识的妹纸比较多,为人热情)叫着帮忙介绍妹子给Jerry认识。性能

上面这个场景来区分下角色:
角色一:目标接口,大龄单身汪找妹子(要干的事情)
角色二:目标对象,单身程序员Jerry(须要找妹子的目标人物)
角色三:代理对象,皮条客隔壁老王(表明Jerry去找妹子)

这里建立三个文件:目标接口 IFindGirl、目标类Jerry、代理类ProxyLaoWan

// IService.java
/** * 目标接口 * Created by Administrator on 2017/9/17 0017. */
public interface IService {
    /** * 找妹子 * @param name 名字 * @param age 年龄 */
    void findGirl(String name, int age);
}

// Jerry.java
/** * 目标对象:单身汪Jerry * Created by Administrator on 2017/9/17 0017. */
public class Jerry implements IService {
    private static final String TAG = "Jerry";

    @Override
    public void findGirl(String name, int age) {
        Log.e(TAG, name + " 说愿意作Jerry的女友");
    }
}

// ProxyLaoWan.java
/** * 代理对象:找对象的代理人老王 * Created by Administrator on 2017/9/17 0017. */
public class ProxyLaoWan implements IService {

    private IService service;

    public ProxyLaoWan(IService service) {
        this.service = service;
    }

    @Override
    public void findGirl(String name, int age) {
        // 老王找到妹子后,再这告诉Jerry
        service.findGirl(name, age);
    }
}复制代码

使用的时候很简单:

// jerry
IService service = new Jerry();
// 建立代理人, 而后把Jerry委托给老王
ProxyLaoWan laoWan = new ProxyLaoWan(service);
// 老王帮Jerry去找妹子
laoWan.findGirl("Tom", 22);复制代码

这个例子中Jerry没有直接去找妹子“Tom”,而是经过了老王,这是一个典型的静态代理模式,Jerry把找妹子的事情委托代理给了老王,一样Jerry若是还有其它的事情,好比买最新的肾phone手机,但是国行的很贵,恰好老王要去香港,又委托老王买港版的iPhone X,因而就要IService目标接口中加入新的要干的事情buyPhone(),一样老王的类、Jerry类都须要实现相应的方法。若是Jerry不断的有新的事情要作,新的功能要扩展那须要修改的地方就比较多了不利于项目的扩展和团队开发。为此这样的需求就产生了动态代理模式。
先来看看套路模板:

// 动态代理
    public void testDynamicProxy(){
        // 目标对象jerry
        final IService jerryService = new Jerry();
        // 代理对象老王
        IService proxyLaoWan = (IService) Proxy.newProxyInstance(
                jerryService.getClass().getClassLoader(),
                jerryService.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 调用目标接口方法的时候,就调用invoke方法
                        long currentTime = System.currentTimeMillis();
                        Object returnValue = method.invoke(jerryService, args);
                        long calledMethodTime = System.currentTimeMillis();
                        long invokeMethodTime = calledMethodTime - currentTime;
                        // 接口方法的执行时间,便于检测性能
                        Log.e("InvocationHandler", "方法执行性能时间:" + invokeMethodTime);
                        return returnValue;
                    }
                });

        // 老王帮忙找妹子,妹子叫Tom 22岁
        proxyLaoWan.findGirl("Tom", 22);

        // 老王帮Jerry买了价值8288元的iPhone X手机
        proxyLaoWan.buyPhone("iPhone X", 8288);
    }复制代码

Jerry若是还要委托老王给买手机,只要给目标接口加入buyPhone方法,而后Jerry实现这个方法,而代理者老王,不须要管都有什么具体的目标接口,经过Proxy.newProxyInstance建立的代理对象,就能够调用目标接口的方法。
介绍下Proxy.newProxyInstance方法:

// Proxy.java
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);复制代码

第一个参数:目标对象的类加载器。由于这个代理对象是运行时才建立的,没有编译时候预先准备的字节码文件提供,因此须要一个类加载器来加载产生Proxy代理里的类类型,便于建立代理对象。
第二个参数:interfaces是目标接口数组
第三个参数:是代理对象当调用目标接口方法的时候,会先回调InvocationHandler接口的实现方法invoke。

到目前为止仍是看不出来Proxy.newProxyInstance是怎么给咱们建立代理对象的,下面分析下它的源码实现:

动态代理的源码实现

class Proxy{
  private final static Class[] constructorParams = { InvocationHandler.class };

  public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        if (h == null) {  throw new NullPointerException(); }
        // 获取到Proxy类的 类类型Class
        Class<?> cl = getProxyClass0(loader, interfaces);
        // 经过Proxy类的类类型对象获取InvocationHandler做为参数的构造方法
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            // 经过构造方法对象建立一个代理对象
            return newInstance(cons, h);
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

    private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
        return cons.newInstance(new Object[] {h} );
    }
}

// Constructor.java
class Constructor{
  public T newInstance(Object... args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (serializationClass == null) {
            // 看最终的实现是Native方法,使用底层NDK来实现建立的代理实例对象
            return newInstance0(args);
        } else {
            return (T) newInstanceFromSerialization(serializationCtor, serializationClass);
        }
    }

    // 底层NDK实现建立代理对象
    private static native Object newInstanceFromSerialization(Class<?> ctorClass, Class<?> allocClass) throws InstantiationException, IllegalArgumentException, InvocationTargetException;

    // 底层NDK实现建立代理对象
    private native T newInstance0(Object... args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException;
}复制代码

从上面代码中咱们能够看出,最终的代理对象的建立是底层NDK来建立返回的,具体就不去看底层的实现了,大致了解到动态代理对象是经过这个构造方法来建立的。

protected Proxy(InvocationHandler h) {
  this.h = h;
}复制代码

通过上门对动态代理模式的一番学习和解释,如今回过头来看

mApiService = retrofit.create(ApiService.class);

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

          @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           }
    }
}复制代码

create方法建立返回的正是ApiService接口的代理对象,每当代理对象调用目标接口里的方法时,动态代理对象就会回调InvocationHandler接口的invoke实现方法。
在Retrofit中,动态代理模式的角色划分:
角色一:目标接口(委托方法),ApiService接口方法
角色二:目标对象(委托方),ApiService.class
角色三:代理对象,create建立的mApiService对象。

至此就把Retrofit中,动态代理业务的网络通信接口讲清楚了,好处就是非入侵的方式,把网络通信api调用代理出来,而后在调用回调的invoke方法里统一处理和准备网络框架须要构建的请求体,做为后续加入到请求队列任务池中进行具体的网络请求。动态代理模式也是AOP的一种实现方式,切片思想的一种。作过Java EE服务端开发的对于Spring的AOP应该深有体会,动态代理与Annotation的结合真是完美,这一点在Retrofit的各类请求方式、参数、url路径等等的注解就体现了。


文章有些长,这是第一篇,后面还会持续更新关于Retrofit的解读,不仅仅是让你懂的Retrofit的原理,还让你学会感觉设计模式的美妙。感谢你的阅读。

相关文章
相关标签/搜索