Android AOP简介及其AspectJ的应用

AOP与OOP

AOP: Aspect Oriented Programming,即面向切面编程。作用是通过预编译方式和运行期动态代理实现程序功能的统一维护。

在Spring开发中,AOP技术应用得比较广泛,通过AOP实现横切关注点(如日志、缓存、事物、安全等),实现与具体影响对象的解耦。AOP也是函数式编程的一种衍生模式。

那什么是切面呢?
说到切面,我们先来说下纵面(也叫纵向)。
OOP(Object Oriented Programming)面向对象设计就是一种典型的纵向编程方式。
1、继承模式比如之类和父类之间的这种关系就是一个很好的展示。
2、app中的一个业务模块内部可以说也是一种纵向表现。
3、分层架构如MVP,每一层之间也是纵向关联在一起的。

一般OOP开发的时候,我们更多关注的是对象Object本身的功能,对象之间的功能和联系往往不会考虑的那么详细。

切面,也叫横向,它所嵌入的地方往往之间并不需要强关联。比如app中各个业务模块之间就属于一种横向的关系。
使用切面的好处:
1、用标签标注切入点,这里的标签我们使用注解(Annotation)完成,由标签引导完成具体功能;
2、解耦。为什么说解耦呢?用打印Log举例,app中可能存在不同的Log框架来实现。如果同一使用Log标签切入,那么在处理Log标签的地方可以统一Log框架。

OOP和AOP往往是搭配使用,功能互补。
OOP中散落着各种功能,而AOP则是将涉及到众多模块的某一类功能进行统一管理。

AOP实现方式

1. JDK动态代理
通过实现InvocationHandler接口,可以实现对一个类的动态代理,通过动态代理,我们可以生成代理类从而在代理类方法中,在执行被代理类方法前后,添加自己的实现内容,从而实现Aop。
2. 动态字节码生成
在运行期,目标类加载后,动态构建字节码文件生成目标类的子类,将切面逻辑加入到子类中,没有接口也可以织入,但扩展类的实例方法为final时,则无法进行织入。如:Cglib。
3. 自定义类加载器
在运行期,目标加载前,将切面逻辑加到目标字节码里。如:Javassist。
4. ASM
ASM可以在编译期直接修改编译出的字节码文件,也可以像Javassit一样,在运行期,类文件加载前,去修改字节码。两者的区别在于,一个将所有需要AOP的类都事先修改了,一个在运行时需要才去修改。
5. AspectJ
和ASM一样,Aspectj有静态编译和动态编译的优点,供程序员选择。另外Aspectj其编码更为简洁,是Android开发中,实现AOP的首选。
6. APT
自定义一个AbstractProcessor,在编译期去解析编译的类,并且根据需求生成一个实现了特定接口的子类(代理类),和JDK动态代理不同的是,APT的代理类是在编译期生成的。

Android中的AOP

  1. APT
    典型应用:Dagger2、BUtterKnife、ARouter;
  2. AspectJ
    功能插入,如日志、权限、埋点等;
  3. Javassist
    应用场景:热更新
    在这里插入图片描述

AspectJ实现原理

参考:AspectJ框架实现原理

AspectJ实例

我们实现一个网络状态检测的AOP。
定义annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNetwork {
}

切入:

@CheckNetwork()
    private void checkNetwork() {
        LogUtil.i("AnnotationFragment", "检测完毕");
    }

处理切入点:

@Aspect
public class CheckNetworkAspect {
    private static final String TAG = CheckNetworkAspect.class.getSimpleName();

    /**
     * 找到处理的切点
     *   * *(..)  “**”表示是任意包名   “..”表示任意类型任意多个参数
     */
    @Pointcut("execution(@la.xiong.androidquick.demo.features.function.annotation.aspect.CheckNetwork  * *(..))")
    public void executionCheckNetwork() {
    }

    /**
     * 处理切面
     *
     * @param joinPoint
     * @return
     */
    @Around("executionCheckNetwork()")
    public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        CheckNetwork annotation = signature.getMethod().getAnnotation(CheckNetwork.class);
        if (annotation != null) {
            Context context = AspectUtils.getContext(joinPoint.getThis());
            if (NetworkUtils.isConnected()) {
                Toast.makeText(context, "当前网络正常", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "此时没有网络连接", Toast.LENGTH_SHORT).show();
            }
            return joinPoint.proceed();
        }
        return null;
    }


}

项目地址

https://github.com/ddnosh/AndroidQuick

猜你喜欢

转载自blog.csdn.net/ddnosh/article/details/100061136
今日推荐