美文网首页
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