Configure AspectJ environment in Android Studio with simple use.

Keywords: Android Java Programming Eclipse

Configure AspectJ environment in Android Studio with simple use.

Use scenarios:

In Android development, we often achieve the following business functions, such as: in a module of app, we need to count the user's use or stay in the module. If we use the idea of OOP to achieve,
Created with Rapha l 2.1.0 Start Time Business Code End Time - Start Time = Time

The simple code is as follows:

public void action1(View v) {
        //Record start time
        long beginTime = System.currentTimeMillis();
        {
            SystemClock.sleep(3000);
            Toast.makeText(this, "Operation 1 was performed", Toast.LENGTH_SHORT).show();
        }
        Log.e(TAG,"Operation 1 executes: " + (System.currentTimeMillis() - beginTime) + "Millisecond" );
    }
There is no doubt that this method can achieve the function of counting the user's time.
    However, the problem of this code structure is also obvious. In the actual project, there may be multiple modules that need to add statistical functions. Then we need to modify the code in each module, and the real business logic may be more complex, so the code readability is very poor.
    And adding statistics to a separate code block is obviously not in line with our OOP programming ideas.
    So you can use the aspect-oriented programming idea here, and you will need to cut out the code block with additional statistical functions separately, and then operate it separately.

Introduction:

AspectJ is an AOP-oriented third-party framework. When compiling java code in AseptJ environment, ajc will be used to replace javac compilation, so Asept changes the code structure during compilation.

Installation:

At AspectJ's official website, you can download: http://www.eclipse.org/aspectj/downloads.php
Download a jar package, double-click can be installed.
When the installation is complete, an aspectj folder will be generated, just like this:

To configure:

Configuring AspectJ under Android Studio is much simpler than configuring it on eclipse. Just add a few lines of groovy code to the gradle script:

1. First add maven Warehouse Path:
repositories {
    mavenCentral()
}
dependencies {
    classpath 'org.aspectj:aspectjtools:1.8.9'
    classpath 'org.aspectj:aspectjweaver:1.8.9'
}
//The above code is in buildscript

2.Create one under your Android project libs Directory, which will be installed in the folder lib Directory aspectjrt.jar Copy in, and then dependencies Compile:
compile files('libs/aspectjrt.jar')
3.The last step is for you. module Of build.gradle Add the following code
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;
            }
        }
    }
}

These codes should be in the official documents, but I didn't find them. I copied it in stack overflow. So far your AspectJ has been configured. sync is ok ay in a minute. Easy!

Use:

1. First, declare a flag, which is a common annotation class 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. Put this sign on the method that needs to be operated:

    @BehaviorTrace("Action2")
    public void action2(View v) {

        {
            SystemClock.sleep(3000);
            Toast.makeText(this, "Operation 2 was performed", Toast.LENGTH_SHORT).show();
        }

    }

ok, which means that this method will be found.

3. Create an operation class:

@Aspect
public class BehaviorAsceptJ {

}

Note that this class must be annotated with @Aspect.

Declare a method as a cut-off point, which needs to be annotated:
@ pointcut(execution(@ full class name * (...)))
The latter two representations * match all method names and two. Representations match all method parameters.

@Pointcut(execution(@com.gjz.aop_aspectj.BehaviorTrace * *(..)))
public void annoBehavior() {
    //Method body can be empty
}

Affirms a method of expressing a section:

@Around("annoBehavior()")
    public Object dealPoint(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object obj = point.proceed();
        Log.e(TAG,"Operation 2 executes: " + (System.currentTimeMillis() - beginTime) + "Millisecond" );
        return obj;
    }

ok, a simple demo is done, and the result is the same as the first code block.

Previously, aspectj implanted bytecode code at compile time.
Is it curious what it did?
Let's decompile it!

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

It is found that the original code block has been replaced, and the dealPoint method is finally executed. The previous code block is extracted into a static method:

  static final void action2_aroundBody0(MainActivity ajc$this, View v, JoinPoint paramJoinPoint)
  {
    SystemClock.sleep(3000L);
    Toast.makeText(ajc$this, Operations performed2", 0).show();
  }

We will not go into the concrete realization.

There should be children's shoes that have been found, which is very similar to dynamic proxy.

We can see the benefits of aspect: simple syntax, compile-time implantation, and no source code intrusion.

Posted by sandingmachine on Tue, 02 Apr 2019 16:33:30 -0700