美文网首页kotlin收藏
Android--AOP架构设计之使用AspectJ监测方法耗时

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

作者: aruba | 来源:发表于2021-11-01 16:24 被阅读0次

    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);
        }
    }
    

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


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

    相关文章

      网友评论

        本文标题:Android--AOP架构设计之使用AspectJ监测方法耗时

        本文链接:https://www.haomeiwen.com/subject/ulnialtx.html