1. 什么是AOP(面向切面编程)
- OOP(面向对象)
我们需要完成一个任务的时候一般都想着把一些操作封装成一个类,所有的变量和操作都封装到一个类里面,那么这个类就是我们的对象,我们要实现某个特定的功能,首先也想着在这个对象里面去实现
缺点
比如我们想实现某些不是常用的功能,我们需要去在需要的对象中去一一实现这些功能,并且我们要不断去维护这些功能,一旦多了我们就会很累的。
比如Android中一些按键统计、生命周期统计,特定统计都是比较琐碎的事情,要利用面向对象的思想去实现都不是很完美,这就要求去一一实现,显得很琐碎
- AOP(面向切面)
我在使用的时候是关注具体的方法和功能切入点,不需要知道也不用关心所在什么类或者是什么对象,我们只关注功能的实现,具体对象是谁,不关心
2.AOP的实现方式
从广义上来讲,AOP技术可以是任何能实现代码织入的技术或框架,对代码的改动最终都会体现在字节码上,而这类技术也可以叫做字节码增强,通用名词理解即可。
- AOP关注的方法功能点,事先不知道所在对象是谁,当然程序的运行都是需要拿到对象在运行的
- 要在知道方法功能点的前提下拿到对象并执行,这就需要用到Java的动态代理
- 动态代理可以在不修改对象的情况下,改变对象的逻辑
java的动态代理:
有两个重要的类或接口
一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),我们可以自己写代码去定义自己的动态代理,去实现AOP,但是太麻烦了,所以需要一个第三方工具
举一个静态代理的例子,快速理解代理
public interface HelloInterface {
void sayHello();
}
public class Hello implements HelloInterface{
@Override
public void sayHello() {
System.out.println("Hello zhanghao!");
}
}
public class HelloProxy implements HelloInterface{
private HelloInterface helloInterface = new Hello();
@Override
public void sayHello() {
System.out.println("Before invoke sayHello" );
helloInterface.sayHello();
System.out.println("After invoke sayHello");
}
}
//调用
HelloProxy helloProxy = new HelloProxy();
helloProxy.sayHello();
结果:
Before invoke sayHello
Hello zhanghao!
After invoke sayHello
3. 实现AOP的方式对比
- 首先,从织入的时机的角度看,可以分为源码阶段、class阶段、dex阶段、运行时织入
- 对于前三项,源码阶段、class阶段、dex织入,由于他们都发生在class加载到虚拟机前,我们统称为静态织入
- 而在运行阶段发生的改动,我们统称为动态织入
| 织入时机 | 技术框架 |
|---|---|
| 静态织入 | APT,AspectJ、ASM、Javassit |
| 动态织入 | java动态代理,cglib、Javassit |
- 静态织入发生在编译器,因此几乎不会对运行时的效率产生影响;
- 动态织入发生在运行期,可直接将字节码写入内存,并通过反射完成类的加载,所以效率相对较低,但更灵活。
动态织入的前提是类还未被加载,你不能将一个已经加载的类经过修改再次加载,这是ClassLoader的限制。但是可以通过另一个ClassLoader进行加载,虚拟机允许两个相同类名的class被不同的ClassLoader加载,在运行时也会被认为是两个不同的类,因此需要注意不能相互赋值, 不然会抛出ClassCastException。
java动态代理、cglib只会创建新的代理类而不是对原有类的字节码直接修改,Javassit可修改原有字节码。
其实利用反射或者hook技术同样可以实现代码行为的改变,但由于这类技术并没有真正的改变原有的字节码,所以暂不在谈论范围内,比如xposed,dexposed。
| First Header | Hook时机 | Android中应用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| Dexposed | 运行时动态hook | 滑动流畅度监控,事件执行监控,热修复 | 可以动态监控和系统通信的各种方法。 | 不支持5.0以上手机 |
| Xposed | 运行时动态hook | 同Dexposed | 可以动态监控和系统通信的各种方法。 | 不支持5.0以上手机,必须root |
| Java Proxy | 运行时动态hook | hook和系统通信接口例如:插件sdk | Java 原生API,没有兼容性问题 | 只能hook 有Interface的类 |
| AspactJ | 编译时修改代码 | 统计方法执行时长,方法前后注入逻辑 | Sprint开源的AOP框架,功能强大。注解很多。基本包括所有的编译时注入方式 | 需要引入118K的jar |
| ASM | 编译时修改代码 | 同上 | 字节码操作库 | 需要自己写注解和编译脚本。字节码插入编写比较费劲 |
| Javassit | 编译时修改代码 | 同上 | 基于java反射的字节码操作类库。对比ASM,编写简单 | 对比ASM,修改类时,执行时间长 |
- 主流使用的都是AspectJ
- 功能强大
- 支持编译期和加载时代码注入
- 易于使用
- 缺陷
- 官方不支持Kotlin, 我们项目基本都是Kotlin编写
不过沪江科技开源了自己优化了的AspectJ, 支持Kotlin, 2.8K赞
gradle_plugin_android_aspectjx
- 目前的开源库中还没有发现可应用于Android平台的比较好的AOP框架或者工具,虽然xposed,dexposed非常强大,但基于严重的碎片化现状,兼容问题永远是一座无法逾越的大山。
- 目前其他的AspectJ相关插件和框架都不支持AAR或者JAR切入的,对于目前在Android圈很火爆的Kotlin更加无能为力。
4. 引入AOP优势和劣势
- 优势
- 可以实现无痕埋点
- AOP天然的面向操作,因此同类型的操作可以快速且无侵入的添加进任何对象
- 完全解耦, 抽取出来的操作, 跟对象再无关联
- 劣势
- 如上表所说, 引入的jar包相对较大
- 使用沪江科技开源的AOP工具是否持续稳定
不错的讲解
- Android AspectJ详解
- 详解AspectJ
- Android AOP 总结
- AOP方式对比选择
- 谈谈Android AOP技术方案
- AOP方式对比选择
- 提到不支持Java8
- 深入理解Android之AOP
- 大神详解
- Android Aspectj 从入门到实战
- 点赞
5. 最终结论
暂时不建议使用
- AOP的最终目的是脱离对象, 对功能点进行抽离
- 这种抽离暂时还不是我们APP的痛点
- 但为了让AOP适应我们APP, 我们需要有一些妥协, 如下
- 原生AspectJ引入顺畅, 但不支持Kotlin, 我们App几乎全部使用Kotlin编写
- 以AspectJ方式支持AOP的暂时只发现沪江科技的aspectjx
- 沪江科技的aspectjx虽然支持Kotlin, 但维护人员似乎只有一个, 在我引入的过程中出现过几个,已经在issue里提到过很多次的问题, 但作者并没有理会, 难免让人怀疑维护者对这个项目的热情, 是否会影响到我们后期的使用