在Android Studio 配置AspectJ环境,与简单使用。

来源:互联网 发布:毕业设计选题系统源码 编辑:程序博客网 时间:2024/04/28 16:38

在Android Studio 配置AspectJ环境,与简单使用。

使用场景:

在Android开发中,我们常常要实现如下的业务功能,如:在app的某一块模块中,需要统计出用户在该模块中使用或停留的时长。若以我们以OOP的思想来实现,
Created with Raphaël 2.1.0开始时间需要统计的业务代码结束时间-开始时间=时长

简单的代码如下:

public void action1(View v) {        //记录开始时间        long beginTime = System.currentTimeMillis();        {            SystemClock.sleep(3000);            Toast.makeText(this, "执行了操作1", Toast.LENGTH_SHORT).show();        }        Log.e(TAG,"操作1执行了: " + (System.currentTimeMillis() - beginTime) + "毫秒" );    }
毫无疑问,这样子可以实现出统计用户时长的功能。    但是,这种代码结构的问题也很明显,在实际项目中,可能有多个模块都要添加统计功能,那么我们就要再每个模块中修改代码,而且现实的业务逻辑可能较为复杂,这样的代码可读性极差。    而且在一个独立的代码块中中添加了统计的功能,这显然不符合我们的OOP编程思想。    所以在这里就可以使用面向切面的编程思想,将需要增加统计功能的代码块单独的切出来,再单独的操作它。

介绍:

AspectJ是一款面向切面(AOP)的第三方框架,当在AseptJ环境下编译java代码的时候,将使用ajc来替换javac编译,所以Asept是在编译期间就改变了代码结构。

安装:

在AspectJ的官方网站可以下载:http://www.eclipse.org/aspectj/downloads.php
下载下来后是一个jar包,双击就可以安装。
安装完成后就会生成一个aspectj文件夹,就像这样子:
这里写图片描述

配置:

在AndroidStudio下配置AspectJ会比在eclipse上配置要简单的多,只要在gradle脚本上添加几行groovy代码即可:

1. 首先添加maven仓库路径:repositories {    mavenCentral()}dependencies {    classpath 'org.aspectj:aspectjtools:1.8.9'    classpath 'org.aspectj:aspectjweaver:1.8.9'}上面这些代码在buildscript内2.在你的安卓项目下创建一个libs目录,将安装文件夹里的lib目录下的aspectjrt.jar拷贝进去,然后在dependencies下编译:compile files('libs/aspectjrt.jar')3.最后一步,在你module的build.gradle里添加以下代码
final def log = project.loggerfinal def variants = project.android.applicationVariantsvariants.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;            }        }    }}

这些代码在官方文档的里面应该就有,但是我没找到 - -。。我是在stackoverflow里拷贝出来的。至此你的AspectJ就已经配置好了。sync一下就ok了。简单吧!

使用:

1.先声明一个标志,就是一个普通的注解类BehaviorTrace:

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by guojianzhong on 2017/2/20. */@Target(ElementType.METHOD)@Retention(RetentionPolicy.CLASS)public @interface BehaviorTrace {    String value();}

2.将这个标志打在需要被操作的方法上:

    @BehaviorTrace("Action2")    public void action2(View v) {        {            SystemClock.sleep(3000);            Toast.makeText(this, "执行了操作2", Toast.LENGTH_SHORT).show();        }    }

ok,这样就表示这个方法将会被找到。

3.创建一个操作类:

@Aspectpublic class BehaviorAsceptJ {}

注意,这个类必须要加上@Aspect注解。

再声明一个方法作为切点,这个方法需要加上注解:
@pointcut(execution(@全类名 * *(. .)))
后面的两个表示*匹配所有的方法名,两个.表示匹配所有的方法参数.

@Pointcut(execution(@com.gjz.aop_aspectj.BehaviorTrace * *(..)))public void annoBehavior() {    //方法体可以为空}

申明一个表示切面的方法:

@Around("annoBehavior()")    public Object dealPoint(ProceedingJoinPoint point) throws Throwable {        long beginTime = System.currentTimeMillis();        Object obj = point.proceed();        Log.e(TAG,"操作2执行了: " + (System.currentTimeMillis() - beginTime) + "毫秒" );        return obj;    }

ok,一个简单的demo就完成了,运行结果跟第一个代码块一样。

之前说aspectj是在编译时就把字节码的代码植入,
是不是很好奇它到底做了什么?
让我们来反编译一下!

  @BehaviorTrace("Action2")  public void action2(View v)  {    View localView = v;JoinPoint localJoinPoint = Factory.makeJP(ajc$tjp_0, this, this, localView);    Object[] arrayOfObject = new Object[3];arrayOfObject[0] = this;arrayOfObject[1] = localView;arrayOfObject[2] = localJoinPoint;BehaviorAsceptJ.aspectOf().dealPoint(new MainActivity.AjcClosure1(arrayOfObject).linkClosureAndJoinPoint(69648));  }

发现原先的代码块已经被替换了,而最终执行的是dealPoint方法,之前的代码块被抽取成了一个静态方法:

  static final void action2_aroundBody0(MainActivity ajc$this, View v, JoinPoint paramJoinPoint)  {    SystemClock.sleep(3000L);    Toast.makeText(ajc$this, 执行了操作2", 0).show();  }

具体的实现我们就不深究了。

应该有童鞋已经发现了,这跟动态代理很类似。

我们可以发现aspect的好处:语法简单,编译时植入,不侵入源代码。

1 0