美文网首页
Android中的AOP编程-AspectJ

Android中的AOP编程-AspectJ

作者: 柒黍 | 来源:发表于2018-04-04 17:19 被阅读0次

    面向切面编程(AOP,Aspect-oriented programming)需要把程序逻辑分解成『关注点』(concerns,功能的内聚区域)。这意味着,在 AOP 中,我们不需要显式的修改就可以向代码中添加可执行的代码块。这种编程范式假定『横切关注点』(cross-cutting concerns,多处代码中需要的逻辑,但没有一个单独的类来实现)应该只被实现一次,且能够多次注入到需要该逻辑的地方。

    代码注入是 AOP 中的重要部分:它在处理上述提及的横切整个应用的『关注点』时很有用,例如日志或者性能监控。这种方式,并不如你所想的应用甚少,相反的,每个程序员都可以有使用这种注入代码能力的场景,这样可以避免很多痛苦和无奈。

    AOP 是一种已经存在了很多年的编程范式。我发现把它应用到 Android 开发中也很有用。经过一番调研后,我认为我们用它可以获得很多好处和有用的东西。

    那么...我们何时何地应用AOP呢?

    一些示例的 cross-cutting concerns 如下:

    • 日志
    • 持久化
    • 性能监控
    • 数据校验
    • 缓存

    这里我们重点使用AspectJ完成一个统计方法执行耗时的功能。

    1.开发环境:

    Android Studio 3.1
    Java 1.7
    Android Plugin 2.3.3
    Gradle 3.3

    2.环境配置

    项目build.gradle文件

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.3'
            classpath 'org.aspectj:aspectjtools:1.9.0'
            classpath 'org.aspectj:aspectjweaver:1.9.0'
        }
    }
    
    allprojects {
        repositories {
            jcenter()
        }
    }
    

    Module中的build.gradle文件

    apply plugin: 'com.android.application'
    
    dependencies {
        compile 'org.aspectj:aspectjrt:1.9.0'
    }
    
    #这里要加入下面这段,原因是要使用它来参与编译
    final def log = project.logger
    final def variants = project.android.applicationVariants
    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    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.7",
                             "-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.功能代码

    我们的目标是监控方法执行时间:

    3.1创建DebugTrace.java

    这是一个注解类,是为了标记我们要追踪的方法

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.CLASS)
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
    public @interface DebugTrace {
    }
    
    3.2创建TimeWatcher.java

    这个类的功能是记录时间

    import java.util.concurrent.TimeUnit;
    public class TimeWatcher {
        private long startTime;
        private long endTime;
        private long elapsedTime;
    
        public TimeWatcher() {
            //empty
        }
    
        private void reset() {
            startTime = 0;
            endTime = 0;
            elapsedTime = 0;
        }
    
        public void start() {
            reset();
            startTime = System.nanoTime();
        }
    
        public void stop() {
            if (startTime != 0) {
                endTime = System.nanoTime();
                elapsedTime = endTime - startTime;
            } else {
                reset();
            }
        }
    
        public long getTotalTimeMillis() {
            return (elapsedTime != 0) ? TimeUnit.NANOSECONDS.toMillis(endTime - startTime) : 0;
        }
    }
    
    3.3创建TraceAspect.java

    这个类是核心类,首先在类上使用@Aspect

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    
    @Aspect
    public class TraceAspect {
    
        private static final String POINTCUT_METHOD = "execution(@leon.training.designpattern.structure.proxy.dynamic.aspectJ.DebugTrace * *(..))";
    
        private static final String POINTCUT_CONSTRUCTOR = "execution(@leon.training.designpattern.structure.proxy.dynamic.aspectJ.DebugTrace *.new(..))";
    
        @Pointcut(POINTCUT_METHOD)
        public void methodAnnotatedWithDebugTrace() {
    
        }
    
        @Pointcut(POINTCUT_CONSTRUCTOR)
        public void constructorAnnotatedDebugTrace() {
    
        }
    
        @Around("methodAnnotatedWithDebugTrace() || constructorAnnotatedDebugTrace()")
        public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            String className = methodSignature.getDeclaringType().getSimpleName();
            String methodName = methodSignature.getName();
    
            final TimeWatcher stopWatch = new TimeWatcher();
            stopWatch.start();
            Object result = joinPoint.proceed();
            stopWatch.stop();
    
            Log.d("TAG", buildLogMessage(methodName, stopWatch.getTotalTimeMillis()));
            return result;
        }
    
        /**
         * Create a log message.
         *
         * @param methodName     A string with the method name.
         * @param methodDuration Duration of the method in milliseconds.
         * @return A string representing message.
         */
        private static String buildLogMessage(String methodName, long methodDuration) {
            StringBuilder message = new StringBuilder();
            message.append("方法 --> ");
            message.append(methodName);
            message.append("   耗时");
            message.append(" --> ");
            message.append("[");
            message.append(methodDuration);
            message.append("ms");
            message.append("]");
            return message.toString();
        }
    }
    

    具体原理和aspectj的使用方法不在这里讨论

    总体来说使用简单,对原生代码侵入性小

    相关文章

      网友评论

          本文标题:Android中的AOP编程-AspectJ

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