Android--AOP架构设计之使用AspectJ监测方法耗时
2021-11-01 本文已影响0人
aruba
AOP为面向切面编程,听着很高大上,实际上就是为了将业务分离,比如我们在网络请求时,需要生成一个签名放入请求头Header,以往的做法是封装一个方法获取签名,AOP只是实现方式不同,将封装方法改为注解形式
AOP概念,将注解的方法、对象等看成是一个连接点,由这些被注解的点构成一个切入点,然后对切入点做具体的处理。这就是三个步骤而已,没什么高端的
AOP也有很多实现方式,AspectJ是利用替换Javac编译器方式,将字节码改变,生成代理类,来达到调用我们注解方法
一、AspectJ注解
使用AspectJ,需要用到它提供的注解:
- Aspect:Aspect 注解类,来表明该类使用AOP进行编程,相当于编译器进行字节码改变的入口
- Pointcut:表示切入点,可以通过通配、正则表达式等指定连接点,使用这些连接点来构成该切入点
还有就是最后一步,具体处理方法使用的注解:
- Before:表示在调用点之前,调用该方法
- After:表示在调用点之后,再调用该方法
- Around:使用该方法代替该点的执行
二、Gradle配置
AspectJ是一个客户端,拥有自己的编译器,所以要在Gradle中指定,使用AspectJ的编译器进行编译
1.在module的gradle最上面添加:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.6'
classpath 'org.aspectj:aspectjweaver:1.9.6'
}
}
plugins {
id 'com.android.application'
}
...
2.上面的gradle中新增编译脚本
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
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;
}
}
}
}
3.依赖库文件
下载地址:https://gitee.com/aruba/aopapplication/blob/master/app/libs/aspectjrt.jar
完整gradle代码如下:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.9.6'
classpath 'org.aspectj:aspectjweaver:1.9.6'
}
}
plugins {
id 'com.android.application'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.aruba.aopapplication"
minSdk 21
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
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 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation files('libs\\aspectjrt.jar')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
三、使用
1.定义注解,来用于表示一个连接点
/**
* 用于注解连接点
* Created by aruba on 2021/11/1.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
String value();
}
2.使用Aspect
首先使用Aspect注解类,再通过Pointcut注解指定该切面中的切入点(这边就是方法),最后使用Around注解来替换原来的方法执行
/**
* Created by aruba on 2021/11/1.
*/
@Aspect
public class AspectBehavior {
//指定使用BehaviorTrace注解的所有包下所有类的所有方法,为一个切入点
@Pointcut("execution(@com.aruba.aopapplication.annotations.BehaviorTrace * *(..))")
public void methodAnnotatedWithBehaviorTrace() {
}
//争对上面切入点methodAnnotatedWithBehaviorTrace的所有连接点进行处理
@Around("methodAnnotatedWithBehaviorTrace()")
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) {
Object ret = null;
try {
//获取方法签名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取注解
BehaviorTrace annotation = methodSignature.getMethod().getAnnotation(BehaviorTrace.class);
//执行之前记录下时间
long startTime = System.currentTimeMillis();
//方法执行
ret = joinPoint.proceed();
//方法执行完后的耗时
long diffTime = System.currentTimeMillis() - startTime;
String clzName = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
Log.d("aruba", String.format("功能:%s 类:%s中方法:%s执行耗时:%d ms", annotation.value(), clzName, methodName, diffTime));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return ret;
}
}
3.使用自定义注解来注解方法
注解方法就是表示该方法为一个连接点
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@BehaviorTrace("上传")
public void upload(View view) {
SystemClock.sleep(300);
}
@BehaviorTrace("下载")
public void download(View view) {
SystemClock.sleep(400);
}
@BehaviorTrace("扫码")
public void scan(View view) {
SystemClock.sleep(500);
}
}
三个按钮点击后的日志如下: