给Android开发人员的AOP文档

一、什么是AOP

关于OOP

大家对于OOP(面向对象编程)肯定不会陌生,核心思想为:万事万物皆对象,OOP推荐开发者把所有的事物当作对象来处理。

假设有一条鱼,我们就可以创建一个Fish类并构造出它的实例来当作是这条鱼并对它进行操作。OOP就是我们把鱼抽象成为Fish类的过程,通过这个过程我们可以获得更加清晰搞笑的逻辑单元划分。

关于AOP

AOP(面向切面编程),这个中文名字翻译的很到位:切面,可以脑部一下假设有一个平面,然后用一个工具去切这个平面,就形成了一个切面的过程。这个平面就是我们的程序,程序里面会有很多功能,像:登录,设置,支付等。假设有一个工具它的作用是检查是否登录,在程序每次支付时要检查是否登录,这个工具就可以像一把刀一样切向支付前的位置,这整个过程就是一个面向切面的过程。

如果你理解了的话其实可以发现OOP和AOP有着本质上的差异。AOP面对的是处理某个过程或步骤,会有切入点,会有切入的动作。然而OOP是一个帮助我们更加清晰搞笑划分事物单元的一种思想。

二、关于AOP的例子

一般我们听到AOP这个名字是在Java后台开发,后台所使用的spring框架一个很核心点就是AOP,通过AOP来解耦。我们作为Android开发人员为什么要去学习这个呢?AOP作为一种编程思想,作用的场景是不区分安卓和后台的,就像各种设计模式,用在合适的地方就能解决痛点。

现在有一个程序,有一个人类的接口,里面有一个方法sayHello()

public interface Person {
    void sayHello();
}

和它的实现类:男人

public class Man implements Person {
    @Override
    public void sayHello() {
        Log.i("AopLog", "你好,Android");
    }
}

然后我们有3个界面,分别是FirstActivity、SecondActivity、ThirdActivity,3个页面中只有同一个操作,就是让一个男人sayHello

public class FirstActivity extends AppCompatActivity {

    Button mSayHello;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frist);

        mSayHello = findViewById(R.id.sayHello);
        mSayHello.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test();
            }
        });
    }

    public void test() {
        Person man = new Man();
        man.sayHello();
    }
}

点击按钮输出的结果为:

你好,Android

其实我们只需要关注3个页面中的test()方法就好了。现在我想要在sayHello的前后分别加一行分割线,让输出的结果变成这样:

---------我是上分割线---------
你好,Android
---------我是下分割线---------

最简单的办法就是直接修改test()方法,像这样:

public void test() {
    Log.i("AopLog", "---------我是上分割线---------");
    Person man = new Man();
    man.sayHello();
    Log.i("AopLog", "---------我是下分割线---------");
}

很好,3个页面,代码要复制3遍,如果有100个这样的界面,那有得你爽。有人会说在Man的sayHello方法里面加,像这样:

public class Man implements Person {
    @Override
    public void sayHello() {
        Log.i("AopLog", "---------我是上分割线---------");
        Log.i("AopLog", "你好,Android");
        Log.i("AopLog", "---------我是下分割线---------");
    }
}

虽然不用复制代码了,但是这就违背了面向对象的思想了,sayHello()方法中做的事情就只有打印”你好,Android”,并不需要包括打印下划线,所以加在这个位置很明显也是不合适的。

静态代理

这个时候我们想到了静态代理模式,可以创建一个代理对象来帮我们完成:

public class PersonProxy implements Person {
    private Person mMan;

    public PersonProxy(Person man) {
        this.mMan = man;
    }

    @Override
    public void sayHello() {
        Log.i("AopLog", "---------我是上分割线---------");
        mMan.sayHello();
        Log.i("AopLog", "---------我是下分割线---------");
    }
}

修改一下test()方法

public void test() {
    Person man = new Man();
    PersonProxy personProxy = new PersonProxy(man);
    personProxy.sayHello();
}

让代理对象来处理,这样就变得比较优雅了,输出结果很明显是:

---------我是上分割线---------
你好,Android
---------我是下分割线---------

使用静态代理来解决已经可以比较好的解决这个问题了,但是我们并不会轻易满足。假设现在又有2个新的接口Car、Computer和他们的实现类

public interface Car {
    void run();
}

public class BmwCar implements Car {
    @Override
    public void run() {
        Log.i("AopLog", "我是一辆宝马骑车,我在快速行驶中");
    }
}



public interface Computer {
    void caculate();
}

public class HuaShuoComputer implements Computer {
    @Override
    public void caculate() {
        Log.i("AopLog", "我是华硕电脑,我善于计算,10 + 10 = 20");
    }
}

要求在Car的run()方法前后 和 Computer的caculate()方法前后都加上下划线。如果这里还要使用静态代理的话就需要创建3个静态代理对象了:PersonProxy、CarProxy、ComputerProxy,这样就非常浪费资源了。

动态代理

我们都知道通过反射可以获取到一个对象中的大部分信息,同时,Java还提供了动态代理方式。主要有一个Proxy类和一个InvocationHandler接口,通过他们可以生成动态代理类或动态代理对象。

一般的动态代理方式

假设为Person创建动态代理,首先我们要新建一个PersonInvocationHandler实现InvocationHandler类

public class PersonInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.i("AopLog", "动态代理啦啦啦");
        return method.invoke(new Man(), args);
    }
}

使用起来也很简单:

public void test() {
    PersonInvocationHandler handler = new PersonInvocationHandler();
    Person manProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),
                                                      new Class[]{Person.class},
                                                      handler);
    manProxy.sayHello();
}

输出接口为:

动态代理啦啦啦
你好,Android

这里要注意2点:
1. 动态代理所有的方法都会经过过调用invoke方法,也就是说如果Person接口中还有另外一个方法的话,当这个方法运行时也会打印一行日志”动态代理啦啦啦”;
2. 动态代理只能使用接口,Proxy.newProxyInstance()方法中第二个参数只能传入接口,这也就是作者为什么要创建Person、Car、Computer这3个没啥用的接口,而不是直接使用他们的实现类。

以上就是动态代理的基本使用方式了,但是上面的代码也只能支持Person的动态代理,还不够,还需要再稍微封装一下来适配Car、Computer或者更多的接口。

让我们新建一个MyInvocationHandler:

public class MyInvocationHandler implements InvocationHandler {
    private Object mObject;

    public MyInvocationHandler(Object target) {
        this.mObject = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.i("AopLog", "---------我是上分割线---------");
        Object result = method.invoke(mObject, args);
        Log.i("AopLog", "---------我是下分割线---------");
        return result;
    }
}

然后再创建一个MyProxy:

public class MyProxy {
    public static Object getProxy(Object target) {
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                      target.getClass().getInterfaces(),
                                      myInvocationHandler);
    }
}

使用:

public void test() {
    Man man = new Man();
    Person manProxy = (Person) MyProxy.getProxy(man);
    manProxy.sayHello();
    Computer computer = new HuaShuoComputer();
    Computer computerProxy = (Computer) MyProxy.getProxy(computer);
    computerProxy.caculate();
    Car car = new BmwCar();
    Car carProxy = (Car) MyProxy.getProxy(car);
    carProxy.run();
}

输出结果:

---------我是上分割线---------
你好,Android
---------我是下分割线---------
---------我是上分割线---------
我是华硕电脑,我善于计算,10 + 10 = 20
---------我是下分割线---------
---------我是上分割线---------
我是一辆宝马骑车,我在快速行驶中
---------我是下分割线---------

很好,大功告成,现在无论是用Person、Car、Computer还是其他更多的接口,都给他们加上了下划线。

总结

  1. 关于AOP的理解,面向切面编程,结合文章的例子,用一张图来表示一下:
    这里写图片描述
  2. 静态代理的使用和动态代理的使用
  3. 文章想要讲清楚AOP的思想,希望不要纠结于动态代理的代码中去。。。
  4. 面向切面编程可以做很多事情,比如:在多个Activity的跳转过程中要检查登录与否,这个跳转可能在Activity的任何一个方法中。这就需要用到第三方AOP框架了。
  5. 谢谢大家阅读
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值