本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Android中的代理模式
代理模式分为静态代理和动态代理。
静态代理含有具体代理类。代理对象调用了被代理对象去执行真正的实现。而动态代理是通过反射机制动态生成代理类,具体代理对象是在运行时生成的。
简单的举例就是,我要做一个事情,但是我不想自己去做,而是使用一个代理类直接或间接的持有我去帮我做这个事情,两者一般是通过实现同样的接口实现代理调用功能。
看看我们平常的MVP模式,它也是一种静态代理的实现,例如IView定义 onLoading
函数,Activity实现这个接口实现了 onLoading
在此方法中展示Loading弹框,而Presenter持有了IView对象,直接调用 onLoading
函数,其实就是调用的Activity 中的onLoading
函数实现了 Loading弹窗效果。
可以看到MVP确实是一种代理模式,但是它和标准的代理模式又有所不同。下面看看标准的代理模式怎么使用吧。
一、静态代理模式
其实固定的套路:
- 定义一个接口来定义需要实现的方法
- 定义一个具体的实现类来实现这个接口,实现接口的方法。
- 定义一个代理类来实现这个接口,并构造函数中传入具体类,在实现的接口方法中调用具体类来调用。
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开发中都有用到,大家看情况使用即可,使用都不难,都是固定的一些模板代码。
好啦,完结!