Aop
概述
AOP(Aspect Oriented Programming)
面向切面编程。就是:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想。
使用场景
针对很多地方都需要用到的模板代码,我们都可以用AOP
来做,比如:
日志、性能监控、埋点等。
举个列子,很多时候我们需要根据当前网络情况做下一步操作,代码如下:
public void checkNetworkNormal(){
if (NetworkUtils.isNetworkAvailable(this)) {
Log.i(TAG, "手机有网,可以进入下一个页面");
} else {
Log.i(TAG,"请检查手机网络设置");
}
}
而如果使用aop
,情况就可以变成下面这样:
@CheckNetwork()
public void checkNetwork(){
Log.i(TAG, "手机有网,可以进入下一个页面");
}
没有网络的情况我们就注解
统一处理了,在需要检查的地方加个注解,而不用在每次需要检查的时候写 if / else
了
方案选型
AOP
在Spring
中使用已很久了,AOP
只是一种方法论,就跟OPP
是一种思想,而Java
是OOP
的一种,AOP
的实践方案有很多,比如:APT
、AspectJ
、Javassist
、ASM
、Xposed
、Dexposed
等;不过在Android
平台上最常用的还是三剑客:APT
、AspectJ
、Javassist
。
方案分类
根据织入时机,我们可以把相关框架分类如下:
织入时机 | 技术框架 |
---|---|
静态织入 | APT,AspectJ、ASM、Javassit |
动态织入 | java动态代理,cglib、Javassit |
他们的区别:
静态织入: 发生在编译期,改变的是class字节码
,因此几乎不会对运行时的效率产生影响;
动态织入:发生在运行期,将字节码写入内存,并通过反射
完成类的加载,所以效率相对较低,但更灵活。
执行时机
- 各方案织入时机图示,一目了然 :-
作者:Czppp
链接:https://www.jianshu.com/p/3de31e13f2fa
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方案对比
总结下各框架的优劣势及学习维护成本,根据需求酌情选择 ~ 。~
技术框架 | 特点 | 开发难度 | 优势 | 不足 |
---|---|---|---|---|
APT | 常用于通过注解减少模板代码,对类的创建于增强需要依赖其他框架。 | ★★ | 开发注解简化上层编码。 | 使用注解对原工程具有侵入性。 |
AspectJ | 提供完整的面向切面编程的注解。 | ★★ | 真正意义的AOP,支持通配、继承结构的AOP,无需硬编码切面。 | 重复织入、不织入问题,不支持java8 |
ASM | 面向字节码指令编程,功能强大。 | ★★★ | 高效,ASM5开始支持java8。 | 切面能力不足,部分场景需硬编码。 |
Javassit | API简洁易懂,快速开发。 | ★ | 上手快,新人友好,具备运行时加载class能力。 | 切点代码编写需注意class path加载问题。 |
java动态代理 | 运行时扩展代理接口功能。 | ★ | 运行时动态增强。 | 仅支持代理接口,扩展性差,使用反射性能差。 |
作者:wanderingGuy
链接:https://www.jianshu.com/p/0acd18d50717
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在Android
上最早应用的可以说是 JakeWharton/hugo ,使用的是AspectJ
,原理介绍的很多
AspectJ的使用
查看最新版本:AspectJ官网
侵入式
一般使用注解
(即在需要插桩的地方添加一个注解
),在指定点改变原有行为的方式。
示例
- 我们先定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface Check {
String[] value() default {};
}
- 注解解释器(如何改变切点的行为)
@Aspect
public class CheckAspectj {
//....
@Around("(method() || constructor()) && @annotation(check)")
public Object aroundJoinPoint(ProceedingJoinPoint joinPoint, Check check) throws Throwable {
//....
}
//....
}
- 在需要的地方添加注解
@Check({"1"})
private void doSomeThing1() {
Log.d(TAG, "passed === doSomeThing1: ");
}
非侵入式
不需要添加注解、通常使用在外部sdk
或系统源码
,我们不方便修改,又希望改变它的原有行为
示例
@Around("call(* android.widget.TextView.setText(java.lang.CharSequence))")
public void handleSetText(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Log.d(TAG," start handleSetText");
proceedingJoinPoint.proceed(new Object[]{"处理过的text"});
Log.d(TAG," end handleSetText");
}
这个就会修改掉我们给
TextView
设置的text
,替换为处理过的text
自定义Gradle插件
看了一些网上的介绍,对于AspectJ
的集成,我们需要用到 AspectJ 的编译器
,而且有一些插件,比如:
classpath 'org.aspectj:aspectjweaver:1.9.7'
classpath 'org.aspectj:aspectjtools:1.9.7'
implementation 'org.aspectj:aspectjrt:1.9.7'
这里面我们可以查看源码得知:jweaver
是包含 tools
,jtools
是包含 jrt
的
通过名字我们就可以看出和Java
类似:JDK
和 JRE
。
gradle-plugin三步曲
编写 groovy 文件
class AOPPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
//....
}
}
resources/META-INFO/gradle-plugins/xxx.properties
implementation-class=com.bobomee.aop_plugin.AOPPlugin
build.gradle
plugins {
id 'groovy'
}
dependencies {
implementation gradleApi()
implementation localGroovy()
implementation 'com.android.tools.build:gradle:7.0.2'
implementation 'org.aspectj:aspectjtools:1.9.7'
}
apply from: "./localRepository.gradle"
到此我们的 自定义
gradle plugin
就完成了,其他地方需要用的时候,只需要apply plugin
就行了
plugins {
id 'com.android.library'
id 'aop' // 我们的插件
}
maven-publish
把插件上传maven
,方便其他项目使用,可以使用 maven-publish
,示例中我 将 annotation
、runtime
、plugin
都编译为maven
形式进行了打包, 用法参考 :maven-publish官网doc
示例
CheckerHolder.setChecker(new Checker() {
@Override
public boolean check(String[] strings, JoinPoint joinPoint) {
if (TextUtils.equals("1", strings[0])) {
Log.e(TAG, "check: === intercept === " + strings[0]);
return true;
} else if (TextUtils.equals("2", strings[0])) {
Log.e(TAG, "check: === intercept === " + strings[0]);
return false;
}
return false;
}
});
@Check({"2"})
private void doSomeThing2(String str) {
Log.d(TAG, "doSomeThing2: === passed == "+str);
}
@Check({"1"})
private void doSomeThing1() {
Log.d(TAG, "passed === doSomeThing1: ");
}
/**
* 在MainActivity的所有生命周期的方法中打印log
*/
@Before("execution(* android.app.Activity.**(..))")
public void lifeCycle(JoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = joinPoint.getThis().getClass().getSimpleName();
Log.e(TAG, "class:" + className+" method:" + methodSignature.getName());
}
示例代码:Github/Aop-Demo
学习资料:深入理解Android之AOP