Android AOP 面向切面编程 - AspectJ
Posted 星火燎原2016
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AOP 面向切面编程 - AspectJ相关的知识,希望对你有一定的参考价值。
AOP 概念
AOP 是 Aspect Oriented Programming 的缩写,意为 面向切面编程,通过预编译和运行期动态代理实现程序功能的统一维护的一种技术。利用 AOP 可以实现对代码的业务逻辑进行隔离,降低各功能间的耦合度。
使用场景: 需求是在类的每个方法中代码执行之前添加一句日志打印,在没有使用 AOP 的情况下,就需要在每个方法中手动添加日志打印,使用了 AOP ,就可以将打印日志代码在编译期间插入方法中,我们维护时二者的逻辑是分开的。
android AOP 的实现方式
AOP 和 OOP 一样只是一种编程思想,它的实现方式主要有以下几种:
- APT :AnnotationProcessor 在编译时生成 Java 文件。
- AspectJ : 在将 .java 文件编译为 .class 文件时进行代码的注入。
- Javassist : 对编译好的 class 字节码文件进行操作。
AOP 术语
JoinPoint:连接点,程序执行过程中明确的点(被拦截的方法,字段,构造器)
Pointcut : 切点,用来描述 JoinPoint 连接点的表达式。比如描述调用 Animal.fly() 方法的地方,则写成
call(* Animal.fly(**))
Advice : 增强,表示在 Pointcut 里面定义的程序点具体要做的操作,通过 before,after,around 来区分是在 Jointpoint 之前,之后还是代替执行的代码。
Aspect :切面,类似 Java 中的类声明,Pointcut 和 Advice 合在一起称作 Aspect。
JoinPoint | 说明 | Pointcut 语法 |
---|---|---|
method call | 函数被调用 | call(MethodSignature) |
method execution | 函数执行内部 | execution(MethodSignature) |
constructor call | 构造函数被调用 | call(MethodSignature) |
constructor execution | 构造函数执行内部 | execution(ConstructorSignature) |
field get | 读成员变量 | get(FieldSignature) |
field set | 写成员变量 | set(FieldSignature) |
static initialization | static 块初始化 | staticinitialization(TypeSignature) |
handler | 异常处理 | handler(TypeSignature)只能与 @Before()配合使用 |
advice execution | advice 执行 | adviceexecution |
Advice | 说明 |
---|---|
@Before(Pointcut) | 执行 JoinPoint 之前 |
@After(Pointcut) | 执行 JoinPoint 之后 |
@AfterReturning | @AfterReturning(pointcut=-“xx”,returning=“returnValue”) |
@AfterThrowing | @AfterThrowing(pointcut=“xx”,throwing=“throwable”) |
@Around(Pointcut) | 替代原来的代码,如果要执行原来的代码,需要使用 ProceedingJoinPoint.proceed() |
AOP 表达式 & 通配符
execution 基本语法:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
除了返回类型模式,方法名模式,参数模式外,其他项都是可选的。
- execution(public * *(…)) : 匹配目标类所有 public 方法, 第一个 * 代表返回类型,第二个 * 代表方法名,而 … 代表任意方法参数。
- execution(* *To(…)) : 匹配目标类所有以 To 结尾的方法,第一个 * 代表任意返回类型,而 *To 代表任意以 To 结尾的方法。
- execution(* com.xing.MainActivity.*(…)) : 匹配 MainActivity 中所有的方法,第一个 * 代表任意返回类型,第二个 * 代表方法名任意。
- execution(*com.xing.demo.MainPresenter+.*(…)) : 匹配 MainPresenter 接口及其所有实现类的方法。
- execution(* com.xing.*(…)) : 匹配 com.xing 包下所有类的所有方法。
- execution(* com.xing…*(…)) : 匹配 com.xing 包,子孙包下所有类的所有方法。
- execution(* com…*.*Dao.find*(…)) : 匹配 com 包,子孙包下以 Dao 结尾的类中所有以 find 为前缀的方法。
Android AspectJ 实现 AOP
Android 中使用 AspectJ 步骤:
(1) 项目根目录 build.gradle 配置 classpath
buildscript
repositories
google()
jcenter()
mavenCentral() // AspectJ 需要 maven
dependencies
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'org.aspectj:aspectjtools:1.8.9'
// classpath 'org.aspectj:aspectjweaver:1.8.9'
(2) app build.gradle 添加依赖和 task
apply plugin: 'com.android.application'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
android
compileSdkVersion 28
defaultConfig
applicationId "com.xing.aspectjsample"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildTypes
release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all variant ->
if (!variant.buildType.isDebuggable())
log.debug("Skipping non-debuggable build type '$variant.buildType.name'.")
return;
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast
String[] args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true))
switch (message.getKind())
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
dependencies
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'org.aspectj:aspectjrt:1.8.9'
AspectJ 实战
实战1 - Activity 生命周期函数中添加 log 打印。
/**
* 定义切面
*/
@Aspect
public class TraceAspect
/**
* 定义切点,拦截 MainActivity 中所有以 on 为前缀的方法
*/
@Pointcut("execution(* com.xing.aspectjsample.MainActivity.on*(..))")
public void onLifecycleLog()
/**
* 在所有以 on 为前缀的方法执行前添加 log.e 打印
*/
@Before("onLifecycleLog()")
public void handleLifecycleLog(JoinPoint joinPoint)
String name = joinPoint.getSignature().getName();
Log.e("MainActivity", name + "-------->>>>>" + joinPoint);
public class MainActivity extends AppCompatActivity
private static final String TAG = "TraceAspect";
private int count = 12;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int count = getCount();
private int getCount()
return count;
@SingleClick
public void click(View view)
Log.e(TAG, "click: ");
Toast.makeText(this, "the button is clicked", Toast.LENGTH_SHORT).show();
@Override
protected void onStart()
super.onStart();
@Override
protected void onResume()
super.onResume();
@Override
protected void onPause()
super.onPause();
@Override
protected void onStop()
super.onStop();
@Override
protected void onRestart()
super.onRestart();
@Override
protected void onDestroy()
super.onDestroy();
实战2 - 防止按钮重复点击
防止按钮重复点击思路是:两次点击的时间间隔小于指定值,不产生点击作用。
定义注解 SingleClick
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick
long value() default 1000;
定义切面类,拦截类中标注了 SingleClick 注解,参数为 View 的方法进行增强处理。
@Aspect
public class SingleClickAspect
private static final String TAG = "SingleClickAspect";
private long lastClickTime;
/**
* 定义切点,标记切点为所有被 @SingleClick 注解修饰的方法
*/
@Pointcut("execution(@com.xing.aspectjsample.SingleClick * *(..))")
void singleClickAnnotated()
/**
* 定义一个 Advice,包裹切点方法
*
* @param joinPoint
*/
@Around("singleClickAnnotated()")
public void handleSingleClick(ProceedingJoinPoint joinPoint) throws Throwable
// 获取方法参数
View view = null;
for (Object arg : joinPoint.getArgs())
if (arg instanceof View)
view = (View) arg;
break;
if (view == null)
return;
// SingleClick 注解只修饰在方法上
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 只处理有 SingleClick 注解修饰的方法
if (!method.isAnnotationPresent(SingleClick.class))
return;
// 获取到 SingleClick 注解对象
SingleClick singleClick = method.getAnnotation(SingleClick.class);
// 获取注解值
long value = singleClick.value();
long currentTime = System.currentTimeMillis();
if (currentTime - lastClickTime > value)
lastClickTime = currentTime;
joinPoint.proceed();
Activity 中进行引用:
@SingleClick
public void click(View view)
Log.e(TAG, "click: ");
Toast.makeText(this, "the button is clicked", Toast.LENGTH_SHORT).show();
以上是关于Android AOP 面向切面编程 - AspectJ的主要内容,如果未能解决你的问题,请参考以下文章
(4.6.23.1)Android之面向切面编程:AOP 与 Aspect简介
AOP 面向切面编程Android Studio 使用 AspectJ 监控方法运行原理分析
字节码插桩Android 打包流程 | Android 中的字节码操作方式 | AOP 面向切面编程 | APT 编译时技术