Using AspectJ in an Android project

Copyright statement: This article is an original article by the blogger and may not be reproduced without the blogger's permission.

Reprint please indicate the source: http://www.cnblogs.com/cavalier-/p/8888459.html

What is AOP

AOP is the abbreviation of Aspect Oriented Programming, that is, aspect-oriented programming. Unlike the object-oriented OOP programming that is usually encountered, OOP is the modularization of functions, and AOP is the unified treatment of problems of the same type. For example, log burying, performance monitoring, dynamic permission control, etc.

AspectJ

AspectJ is actually the practice of AOP programming. There are still many AOP implementations, such as ASMDex, but the author chooses AspectJ.

Using AspectJ in an Android project

If you use native AspectJ to configure in the project, it will be very troublesome. There is an open source SDK gradle_plugin_android_aspectjx on GitHub based on gradle configuration.

Access instructions

Please check the access configuration process in the open source project by yourself

Introduction to Join Points in AspectJ

Join Points are a key concept in AspectJ. Join Points can be regarded as an execution point when the program is running. For example, a function call can be regarded as a Join Points, which is equivalent to a code entry point. But in AspectJ, only the following execution points are considered Join Points:

Join Points illustrate example
method call function call For example, calling Log.e(), which is a Join Point
method execution function execution For example, inside the execution of Log.e() is a Join Points. Note that here is the inside of the function
constructor call constructor call Similar to method call
constructor execution constructor execution Similar to method execution
field get get a variable For example, read the DemoActivity.debug member
field set set a variable For example, set the DemoActivity.debug member
pre-initialization Some of the work that Object does in the constructor. -
initialization The work that Object does in the constructor. -
static initialization class initialization Such as class static{}
handler exception handling For example, in try catch, the execution in the corresponding catch
advice execution This is the content of AspectJ -

Introducing Pointcuts

A program will have multiple Join Points, even if the same function is divided into call and execution types of Join Points, but not all Join Points are what we care about, Pointcuts is to provide a way for developers to choose value The desired JoinPoints method.

Advice

Advice is the way in which the code we insert can be inserted, including Before, After, and Around.
Here's an example:

@Before(“execution(* android.app.Activity.on**(..)))”)
public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{
}

This will be divided into several parts, let's look at them in turn:

  • @Before: Advice, which is the specific insertion point
  • execution: handle the type of Join Point, such as call, execution
  • (* android.app.Activity.on**(..)): This is the most important expression. The first one *represents the return value, which *means that the return value is of any type. The latter is a typical package name path, which can *contain for wildcarding, and *there is no difference between several. At the same time &&、||、!, conditions can be combined here. () represents the parameters of this method, you can specify the type, such as android.os.Bundle, or (..) to represent any type and any number of parameters.
  • public void onActivityMehodBefore: The code that actually cuts in.

Before and After are actually well understood, that is, insert code before and after Pointcuts, then Android, literally, inserting code before and after the method, it includes all the functions of Before and After, code show as below:

@(“execution(* com.xys.aspectjxdemo.MainActivity.testAOP()))”)
public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    String key = proceedingJoinPoint.getSignature().toString();
    Log.d(TAG,”onActivityMethodAroundFirst:”+key);
    proceedingJoinPoint.proceed();
    Log.d(TAG,”onActivityMethodAroundSecond:”+key);
}

In the above code, proceedingJoinPoint.proceed() represents the execution of the original method, before and after, various logical processing can be performed.

Custom Pointcuts

Custom Pointcuts allow us to cut into one or more specified entry points more precisely.
First we need to define an annotation class

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface DebugTrace {
}

Add this annotation where you need to insert code, for example in MainActivity:

public class MainActivity extends AppCompatActivity{
    final String TAG = MainActivity.class.getSimpleName();
    
    @Override
    protedcted void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        logTest();
    }

    @DebugTrace
    public void logTest(){
        Log.e(TAG,”log test");
    }
}

Finally create the cut-in code

@Pointcut(“execution(@com.kun.aspectjtest.aspect.DebugTrace * *..*.*(..))”)
public void DebugTraceMethod(){}

@Before(“DebugTraceMethod()”)
public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable{
    String key = joinPoint.getSignature().toString();
    Log.e(TAG, “beforeDebugTraceMethod:”+key);
}

Call

In the pointcut expression of AspectJ, we have used execution before, and there is actually a type - call, so what is the difference between these two syntaxes? For call:

Call (Before)
Pointcut{
    Pointcut Method
}
Call (After)

For Execution:

Pointcut{
    execution (Before)
        Pointcut Method
    execution (After)
}

Withincode

This syntax is usually used to filter some pointcut conditions for more precise entry control, as follows:

public class MainActivity extends AppCompatActivity{
    final String TAG = MainActivity.class.getSimpleName();
    
    @Orveride
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        aspectJ1();
        aspectJ2();
        aspectJ3();
    }
    public void aspectJTest(){
        Log.e(TAG,”execute aspectJTest");
    }
    
    public void aspectJ1(){
            aspectJTest();
    }
    public void aspectJ2(){
            aspectJTest();

    }
    public void aspectJ3(){
            aspectJTest();
    }
}

aspectJ1(), aspectJ2(), and aspectJ3() all call the aspectJTest method, but only want to insert code when aspectJ2 calls aspectJTest. At this time, you need to use the combination of Pointcut and withcode to accurately locate the entry point.

@Pointcut(“(call(* *..aspectJTest()))&&withincode(* *..aspectJ2())”)
public void invokeAspectJTestInAspectJ2(){
}

@Before(“invokeAspectJTestInAspectJ2()”)
public void beforeInvokeaspectJTestInAspectJ2(JoinPoint joinPoint) throws Throwable{
    Log.e(TAG,”method:”+getMethodName(joinPoint).getName());
}

private MethodSignature getMethodName(JoinPoint joinPoint){
    if(joinPoint == null) return null;
    return (MethodSignature) joinPoint.getSignature();
}

execution syntax

execution() is the most commonly used pointcut function, and its syntax is as follows:
For example, the following syntax:
@Around(“execution(* *..MainActivity+.on*(..))")
The entire expression can be divided into five parts:

  1. execution() is the expression body
  2. The first *sign represents the return type, and the * sign represents all types.
  3. The package name indicates the package name that needs to be intercepted, and *. is used here to match all package names.
  4. The second * sign indicates the class name, followed by .MainActivity refers to the specific class name MainActivity.
  5. *(..)The last asterisk represents the method name, +. represents the specific function name, the *sign wildcard includes the parameters of the method in brackets, and the two dots represent any parameters.

errors encountered

  1. The following errors can be solved using gradle2.2.3, which is caused by not yet adapting to gradle3.0
Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.
> Unexpected scopes found in folder '/Users/ram/WorkSpace/AndroidWorkSpace/MyDemo/app/build/intermediates/transforms/AspectTransform/debug'. Required: PROJECT, SUB_PROJECTS, EXTERNAL_LIBRARIES. Found: EXTERNAL_LIBRARIES, PROJECT, PROJECT_LOCAL_DEPS, SUB_PROJECTS, SUB_PROJECTS_LOCAL_DEPS

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326074808&siteId=291194637