Android常用设计模式-代理模式与动态代理

215 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Android中的代理模式

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

静态代理含有具体代理类。代理对象调用了被代理对象去执行真正的实现。而动态代理是通过反射机制动态生成代理类,具体代理对象是在运行时生成的。

简单的举例就是,我要做一个事情,但是我不想自己去做,而是使用一个代理类直接或间接的持有我去帮我做这个事情,两者一般是通过实现同样的接口实现代理调用功能。

看看我们平常的MVP模式,它也是一种静态代理的实现,例如IView定义 onLoading 函数,Activity实现这个接口实现了 onLoading 在此方法中展示Loading弹框,而Presenter持有了IView对象,直接调用 onLoading 函数,其实就是调用的Activity 中的onLoading 函数实现了 Loading弹窗效果。

可以看到MVP确实是一种代理模式,但是它和标准的代理模式又有所不同。下面看看标准的代理模式怎么使用吧。

一、静态代理模式

其实固定的套路:

  1. 定义一个接口来定义需要实现的方法
  2. 定义一个具体的实现类来实现这个接口,实现接口的方法。
  3. 定义一个代理类来实现这个接口,并构造函数中传入具体类,在实现的接口方法中调用具体类来调用。

1.定义接口

interface IPlayBall {
    public void play();
}

2.定义具体的实现类

public class PlayBasketball implements IPlayBall {

    @Override
    public void play() {
        Log.d("PlayBasketball", "I play Basketball。");
    }
}

3.定义代理类的两种方式

public class LogProcessorProxy implements IPlayBall {
    // 代理类持有被代理类的引用
    private IPlayBall playBasketball = new PlayBasketball();

    @Override
    public void play() {
        //通过具体类来调用
        mainLogPrinter.play();
    }
}

//当然我们可以通过构造方式的形式来传入具体的对象来执行
public class LogProcessorProxy2 implements IPlayBall {
    private IPlayBall iPlayBall;

    public LogProcessorProxy2(IPlayBall iPlayBall) {
        this.iPlayBall = iPlayBall;
    }

    @Override
    public void play() {
       iPlayBall.play()
    }
}

使用:

    //具体实现类已经在代理类中定义好了
	IPlayBall proxy = new LogProcessorProxy();
	proxy.play();

    //通过构造的方式传入具体实现类(更推荐这种,更灵活)
    IPlayBall playBasketball = new PlayBasketball();
    IPlayBall proxy = new LogProcessorProxy2(playBasketball);
	proxy.play();

二、动态代理

可以看到标准的静态代理不管怎么变化都是持有的具体实现类,去操作,而动态代理的实现其实就是基于Java的Api Proxy.newProxyInstance 来实现的。

我们先定义一个动态代理类

public class DynamicProxy implements InvocationHandler {
    private  Object object;

    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = method.invoke(object,args);
            return result;
    }
}

调用的方式


    //动态代理
    IPlayBall playBasketball = new PlayBasketball();
    DynamicProxy dynamicProxy=new DynamicProxy(playBasketball);
    IPlayBall proxy=(IPlayBall) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IPlayBall.class},  dynamicProxy);
    proxy.buyCar();

内部原理:

InvocationHandler handler = new InvocationHandlerImpl(..);
其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象

Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
通过反射从生成的类对象获得构造函数对象

Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
通过构造函数对象创建动态代理类实例

实际上因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程。

InvocationHandler handler = new InvocationHandlerImpl(..);
其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用

Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
通过 Proxy 直接创建动态代理类实例


我们常用的Retrofit就是通过动态代理创建Service对象 Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },new InvocationHandler() )

invoke中的代码就是当网络接口被调用的时候需要做的处理。在这个方法里,首先根据接口定义的方法,生成一个ServiceMethod对象,在ServiceMethod对象中会反射接口中定义的注解,解析出具体的网络请求方式,然后拿到封装好的ServiceMethod对象后,构造一个OkHttpCall对象,以便与进行真正的网络请求

总结

可以看到静态代理就是比较直接,直接拿到对象调用,而动态代理比较委婉,是在运行中动态生成的对象,其本质是JDK的动态代理机制是利用动态在内存中生成类字节码的方式实现的,将分散的不同对象的不同方法的调用转发到该动态类中进行处理。

最后静态代理和动态代理在Android开发中都有用到,大家看情况使用即可,使用都不难,都是固定的一些模板代码。

好啦,完结!