Android--AOP架构设计之使用AspectJ监测方法耗时

2021-12-06 17:38:00 浏览数 (1)

AOP为面向切面编程,听着很高大上,实际上就是为了将业务分离,比如我们在网络请求时,需要生成一个签名放入请求头Header,以往的做法是封装一个方法获取签名,AOP只是实现方式不同,将封装方法改为注解形式
AOP概念,将注解的方法、对象等看成是一个点,由这些被注解的点构成一个面,然后对这个面做具体的处理。这就是三个步骤而已,没什么高端的
AOP也有很多实现方式,AspectJ是利用替换Javac编译器方式,将字节码改变,来达到调用我们注解方法
一、AspectJ注解

使用AspectJ,需要用到它提供的注解:

  • Aspect:Aspect 注解类,来表明该类使用AOP进行编程,相当于编译器进行字节码改变的入口
  • Pointcut:表示一个面,可以通过通配、正则表达式等指定,使用这些点来构成该面

还有就是最后一步,具体处理方法使用的注解:

  • Before:表示在调用点之前,调用该方法
  • After:表示在调用点之后,再调用该方法
  • Around:使用该方法代替该点的执行
二、Gradle配置

AspectJ是一个客户端,拥有自己的编译器,所以要在Gradle中指定,使用AspectJ的编译器进行编译

1.在module的gradle最上面添加:
代码语言:javascript复制
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中新增编译脚本
代码语言:javascript复制
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代码如下:
代码语言:javascript复制
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.定义注解,来用于表示一个点
代码语言:javascript复制
/**
 * 用于注解点
 * Created by aruba on 2021/11/1.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BehaviorTrace {
    String value();
}
2.使用Aspect

首先使用Aspect注解类,再通过Pointcut注解指定该切面中的点(这边就是方法),最后使用Around注解来替换原来的方法执行

代码语言:javascript复制
/**
 * 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.使用自定义注解来注解方法

注解方法就是表示该方法为一个点

代码语言:javascript复制
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);
    }
}

三个按钮点击后的日志如下:

项目地址:https://gitee.com/aruba/aopapplication/tree/master

0 人点赞