AOP之aspectj
aop,英文全称为Aspect Oriented Programming,意思是面向切面编程,是一种高内聚,低耦合的编程思想。在很多业务中都有广泛的应用。
业务场景
有这么一个需求,要统计activity中的oncreate方法耗时,为后续做卡顿优化提供数据支撑的基础。
- 一般实现,在每个activity的oncreate方法中添加统计耗时代码,比如
@Override
protected void onCreate(Bundle savedInstanceState) {
long start = System.currentTimeMillis();//1
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, " spend time =" + (System.currentTimeMillis() - start));//2
}
想象一下,每个activity中的oncreate方法都多出1,2步骤的代码,如果是所有activity,所有的生命周期都要统计呢,是不是感觉这个工作很繁琐,并且重复,无意义。程序员是比较懒的一群生物,他会想办法偷懒,所以,aspectj这时候就出现了。
有aspectj后的业务代码
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
如果有了aspectj技术以后,那么业务程序员还是一如既往的正常编写业务代码,无需添加任何统计代码,那么统计的需求如何实现呢。为了让例子更简单易懂,我们这里还是只统计oncreate,避免代码太多,所带来的迷糊困惑
aspectj
引入
伟大的开源社区其实有非常好的aspectj的sdk,这里我推荐hujiang,开源地址https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx
该项目是在注解的基础上实现aop的,记住是注解,这是他的巨大优势(易上手),同时也有点缺陷(有一定的业务代码入侵)
开始
代码工程地址demo
- 首先在项目工程的build.gradle中引入配置
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
- 然后再在app中的build.gradle中引入
implementation "org.aspectj:aspectjrt:1.8.9"
apply plugin: 'android-aspectjx'
aspectjx {
include 'com.example'
}
然后就可以开始愉快的编写aop代码了
aspectj代码
@Aspect
public class SpendTimeAspect {
@Pointcut("execution(* *.onCreate(..))")
public void injectSpendCode() {
//生命所有的类中oncreate方法,作为切入点
}
@Around("injectSpendCode()")
public void aroundOnCreate(ProceedingJoinPoint joinPoint) throws Throwable {
//插入的代码
long start = System.currentTimeMillis();
//执行原来的代码
Object[] params = joinPoint.getArgs();
joinPoint.proceed(params);
//插入的代码
Log.d("SpendTimeAspect","method spend "+(System.currentTimeMillis()-start));
}
}
讲解一下代码步骤
- 首先新建一个Java,名字自己取就好,这里是SpendTimeAspect
- 使用注解@Aspect来声明类,这里的作用是告诉编译器,这个类要插桩行为
- 使用注解@Pointcut来声明哪个类哪个方法要进行插桩,规则匹配支持通配符。其中*.onCreate(..)的意思就是所有类的oncreate方法要进行插桩,该方法无需任何实现
- 使用注解@Around来声明要插入的代码,就像以前一样正常编写java,这里的代码都会被插桩过去。
代码效果大家可以拷贝工程下来运行看一下,有兴趣研究aspectj源码的同学,可以继续深究一下,你会发现aspectj的实现也是asm来实现的,所以asm是个更加强大的aop实现技术方案,只不过入门门槛高。后续也会有文章来讲解这个,然后hujiang这个插件在aspectj基础上,配合google的transform流程
aspectj注解语法
其实,大多数业务情况,掌握我上面所用到的例子中的语法就可以完成很多很多黑科技了。本着文章的完整性,还是系统性讲一次语法相关的吧。
- @Aspect 用它声明一个类,表示一个需要执行的切面。
- @Pointcut 声明一个切点。
- @Before/@After/@Around/@AfterReturning/@AfterThrowing(统称为Advice类型) 声明切面代码的执行顺序,建议使用around,可以满足大部分需求
@Aspect
直接声明一个类,这样子才会执行这个类里面的插桩语法
@Pointcut
指定了一个代码织入点,注解内的execution(* .onCreate(..))是一个切点表达式,第一个号表示返回值可为任意类型,后跟包名+类名+方法名(可以用*代表全匹配的意思),括号内表示参数列表, .. 表示匹配任意个参数
- Method call:方法被调用
- Method execution:方法执行
- Constructor call:构造函数被调用
- Constructor execution:构造函数执行
- Static initialization:static 块初始化
- Field get:读取属性
- Field set:写入属性
- Handler:异常处理
@Advice
- @Before:切入点前织入
- @After:切入点后织入,无论连接点执行如何,包括正常的 return 和 throw 异常
- @AfterReturning:只有在切入点正常返回之后才会执行,不指定返回类型时匹配所有类型
- @AfterThrowing:只有在切入点抛出异常后才执行,不指定异常类型时匹配所有类型
- @Around:替代原有切点,如果要执行原来代码的话,调用 ProceedingJoinPoint.proceed()
语法大全
总结
aspectJ是一个容易上手来实现aop的技术方案,赶紧动起小手,自己一步一步的写个demo调试一下。也欢迎在我的demo基础上,动手实验.
希望我的文字能给你带来帮助,同时也欢迎关注个人公众号,我的所有文章第一手信息回优先发布这里。
个人公众号:河边的小黑屋