Android aop工作原理

作者: 唠嗑008 | 来源:发表于2019-12-06 16:06 被阅读0次

    AOP的基本概念

    先来看看百度百科对AOP的解释

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

    关键点解析:

    • 1、面向切面编程,这一点与我们熟知的面向对象编程(OOP)是不一样的;
    • 2、主要实现方式:预编译运行期动态代理
    • 3、aop算是对oop的一个补充,能解决oop在设计上的一些不足;
    • 4、让代码更解耦,同时提升开发效率

    OOP与AOP的对比

    上面提到AOP也是一种编程思想,只是这种思想和OOP有所区别,算是对OOP的一种补充。下面接先来看看二者的区别与联系:

    AOP与OOP的区别

    OOP:

    • 关注点是:对象。

    • 解决问题类型:对于业务处理过程中的实体,可以把的属性和行为进行抽象封装,还可以通过继承与多态来定义对象之间的联系。

    • 解决问题的方式:通过操作对象来完成业务功能。

    • 思想结构:解决竖向的问题。

    AOP:

    • 关注点是:切面。

    • 解决问题类型:对业务处理过程的切面的提取,换句话说是针对业务处理过程的某个步骤,让业务逻辑各部分之间实现低耦合;它能在不侵入源码的情况下解决某一类问题。

    • 解决问题的方式:将业务逻辑中一些通用的功能用切面的方式提取出来,多个业务模块可以共享这个通用行为,即使这个行为发生改变,也不用修改那些业务模块,只需修改这个切面即可。

    • 思想结构:解决横向的问题。

    AOP的优点

    解耦效率

    所谓解耦是指AOP能把功能模块中与核心业务逻辑无关的代码抽离出来,比如说很多业务模块都会包含日志、缓存、性能检测等通用化的功能,即使你做了封装,依然还是耦合的,而AOP可以将它们与核心业务逻辑分离,这样程序维护性和扩展性都会提高。

    所谓效率是指提升了开发效率却不几乎影响程序运行效率。在OOP中接口方法的变动会带来大的改动;而AOP可以做到一处修改,处处使用,这毫无疑问提升了开发效率,而且AOP(AspectJ)是编译期插入字节码,所以对性能也没什么影响。

    AOP与OOP的联系

    很多刚接触AOP的人,会有一个”错觉“,觉得AOP 能做到的,一个定义良好的 OOP 的接口也一样能够做到。上面提到过AOP是解决横切问题的,诚然OOP也可以把那些复杂的业务逻辑(需要多个类整合一起处理业务)封装起来,对外提供一个简洁的接口,但是无论多完美的抽象封装,最后都会去调用接口的方法,尤其是在多个业务模块中多次调用的时候,这就会造成耦合,而且一旦接口进行修改,那就只能多处修改,让事情变的糟糕;但是AOP不会这样,一旦行为变更,你只需要去修改切面Aspect,再重新编织(weave)即可。AOP与OOP没有孰强孰弱之分,AOP是OOP的一个补充,二者有不同的应用场景,在项目中结合起来能更好的提升开发效率,降低代码耦合

    AOP在Android上的实践方案

    如同OOP一样,AOP也是一种编程思想,而实现AOP的方案也很多,在Android平台最常用的三剑客:APT、AspectJ 、Javassist

    APT
    首先,APT(Annotation Processing Tool)是javac提供的一种工具,它在编译时扫描、解析、处理注解。它会对源代码文件进行检测,找出用户自定义的注解,根据注解、注解处理器和相应的apt工具自动生成代码。这段代码是根据用户编写的注解处理逻辑去生成的。最终将生成的新的源文件与原来的源文件共同编译(注意:APT并不能对源文件进行修改操作,只能生成新的文件,例如往原来的类中添加方法)。代表框架:DataBinding,Dagger2, ButterKnife, EventBus3, 具体流程图如下图所示:

    image.png

    注意:APT针对的是编译时注解,代码生成时机是:编译期,源码阶段.java

    推荐阅读我之前的文章:
    Android编译时注解--入门篇(AbstractProcessor、APT)
    Android APT工作原理(annotationProcessor)

    AspectJ
    AspectJ是Aspect(切面)在Java上的一种实现方案(Kotlin本质也算java)。AspectJ有两种实现方式,一种是直接使用AspectJ语言编写,然后使用ajc编译器用来编译.aj文件,生成java标准的class文件;一种是使用Java语言和注解,然后通过AspectJ提供的织入器(aspectjweaver),把代码织入到目标class文件。

    AspectJ语言的语法和Java一样,只是比Java语言多了一些关键字。但是.aj文件只有ajc编译器才识别,而Android编译器不认识这种文件,所以在Android开发中使用注解的方式来实现。

    在Android中使用AspectJ需要在Gradle做一大堆配置,而且兼容性不太好,不过沪江公司推出了一种集成方案gradle_plugin_android_aspectjxaspectjx算是目前使用最广泛且兼容性最好的方案了,他的优势:AspectJX是一个基于AspectJ并在此基础上扩展出来可应用于Android开发平台的AOP框架,可作用于java源码,class文件及jaraar包,同时支持kotlin的应用。

    注意:AspectJ是在编译期以字节码的形式插入目标.class,也就是.class转换成dex文件之前

    Javassist
    Javassist作用是在编译器间修改class文件,具体说是在class文件被转化为dex文件之前。通过自定义Gradle插件Transform API 来修改原class文件。(这部分内容,暂时还不是特别熟悉,后面会做修改补充的。)

    AspectJ工作原理

    AspectJ的核心是ajc编译器 (aspectjtools)织入器 (aspectjweaver)

    ajc编译器:
    基于Java编译器之上的,它是用来编译.aj文件,aspectjJava编译器的基础上增加了一些它自己的关键字和方法。因此,ajc也可以编译Java代码。

    weaver织入器:
    为了在java编译器上使用AspectJ而不依赖于Ajc编译器,aspectJ 5出现了 @AspectJ,使用注释的方式编写AspectJ代码,可以在任何Java编译器上使用。 由于Android Studio默认是没有ajc编译器的,所以在Android中使用@AspectJ来编写。

    它的工作原理是:通过Gradle Transform API,在class文件生成后至dex文件生成前,遍历并匹配所有符合AspectJ文件中声明的切点,然后将Aspect的代码织入到目标.class。织入代码后的新.class会加入多个JoinPoint,这个JoinPoint会建立目标.classAspect代码的连接,比如获得执行的对象、方法、参数等。

    目标class大概这样

    class && jPoint.png

    通过描述可知,整个过程发生在编译期,是一种静态织入方式,所以会增加一定的编译时长,但几乎不会影响程序的运行时效率。

    使用AspectJX需要注意的一些地方:

    1、编译速度优化建议
    之前说过AspectJ织入代码会增加一定的编译时间,如果项目比较庞大,比如引入大量的三方,组件化时,编译速度就会很慢,所以需要做一些优化,具体建议如下:

    • 在写切点表达式的时候,尽可能使用精确的匹配规则;
    • 尽可能排除所有不需要扫描的文件包,比如各种第三方库。

    2、如果把 AspectJ 代码单独放到一个 library module 的话,library module 还需要添加 compile 'org.aspectj:aspectjrt:1.8.+' 依赖。

    3、AspectJ 的原理是在编译期注入代码,所以只能切入项目class、 jar 或 aar,不能注入 Android 平台 .jar注意像v4、v7、v13、androidx都属于第三方(只是Google的扩展库而已),是可以插入的。例如,可以在 support 包的 Fragment 中注入代码,但是无法在 Activity 中注入代码,只能注入项目的继承自 Activity 的 XXActivity。

    讨论一个问题

    这是网上流传的一张图,作者把AspectJ的代码注入时机放在.java.class阶段,也就是说译期间直接修改源代码生成class。我认为是不对的,应该是class转dex这个阶段,原因有2点,第一:是编译期会javac目录下产生的class文件和transform目录下的内容是不一样的,前者是没有插入代码的,后者是插入代码的;第二:aspectj可以切入jar和aar,这明显是针对class的啊。不知道我的理解是否有误?欢迎大家讨论。

    总结:
    1、因为OOP在设计上存在一些不足,所以有了AOP的解决方案,二者是相辅相成的。

    2、AOP可以把那些横跨并嵌入众多模块里的功能放在一个Aspect切面来处理,利用aspectjx不仅可以修改项目class,还可以操作jaraar的class。

    3、AOP的实践方案有很多,比如:APT、AspectJ、Javassist、ASM、Xposed、Dexposed等;不过在Android平台上最常用的还是三剑客:APTAspectJJavassist

    更多AOP实践方案和字节码插桩的文章会在后面文章中讲解,请大家继续关注!

    写文不易,如果本文对你有所帮助,麻烦点个 关注

    参考:
    AOP之AspectJ在Android中的应用
    AOP与OOP有什么区别,谈谈AOP的原理是什么
    Android AOP编程的四种策略探讨:Aspectj,cglib+dexmaker,Javassist,epic+dexposed

    相关文章

      网友评论

        本文标题:Android aop工作原理

        本文链接:https://www.haomeiwen.com/subject/mpufgctx.html