What is AOP
AOP is the abbreviation of Aspect Oriented Programming, that is, aspect-oriented programming. Unlike the object-oriented OOP programming we usually encounter, OOP modularizes functions, and AOP is unified in dealing with problems of the same type. For example, log burial, performance monitoring, dynamic permission control, etc.
AspectJ
AspectJ is actually a practice of AOP programming. There are still many AOP implementations, such as ASMDex, but the author chooses AspectJ.
Using AspectJ in Android Project
It will be very troublesome if you use native AspectJ to configure it in your project. There is an open source SDK on GitHub gradle_plugin_android_aspectjx based on gradle configuration.
Access instructions
Please check the access configuration process in the open source project by yourself
Introduction to Join Points of AspectJ
Join Points is a key concept in AspectJ. Join Points can be regarded as an execution point during the program run. For example, the call of a function can be regarded as a Join Points, which is equivalent to the code entry point. But in AspectJ, only the following execution points are considered Join Points:
Join Points | illustrate | Example |
---|---|---|
method call | Function Calls | For example, calling() is a Join Point |
method execution | Function execution | For example, within the execution of () is a Join Points. Note that this is inside 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, reading members |
field set | Set a variable | For example, set up members |
pre-initialization | Some work done by Object in the constructor. | - |
initialization | The work that Object does in the constructor. | - |
static initialization | Class initialization | For example, the static{} of the class |
handler | Exception handling | For example, in a try catch, the corresponding execution in the catch |
advice execution | This is the content of AspectJ | - |
Pointcuts Introduction
A program will have multiple Join Points. Even if the same function is divided into call and execution type Join Points, but not all Join Points are what we care about. Pointcuts is a method that enables developers to select the required JoinPoints.
Advice
Advice is how the code we insert can be inserted, including Before, After, Around.
Here is an example:
@Before(“execution(* **(..)))”) public void onActivityMethodBefore(JoinPoint joinPoint) throws Throwable{ }
Here it will be divided into several parts, let's look at it in turn:
- @Before: Advice, that is, the specific insertion point
- execution: handles the type of Join Point, such as call, execution
- (* **(..)): This is the most important expression. The first * represents the return value, and * represents the return value of any type. The latter is a typical package name path, which can contain * for wild-allocation, and there is no difference between several *. At the same time, you can pass &&, ||, here! to perform conditional combination. () represents the parameters of this method. You can specify the type, for example, or (..) to represent any type and any number of parameters.
- public void onActivityMehodBefore: The actual code to enter.
Before and After are actually easy to understand, that is, insert code before and after Pointcuts. So, Android, literally speaking, insert code before and after the method. It contains all the functions of Before and After, the code is as follows:
@(“execution(* ()))”) public void onActivityMethodAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ String key = ().toString(); (TAG,”onActivityMethodAroundFirst:”+key); (); (TAG,”onActivityMethodAroundSecond:”+key); }
In the above code, () represents the execution of the original method, and various logical processing can be performed before and after this.
Custom Pointcuts
Custom Pointcuts allows us to cut into one or more specified entry points more accurately.
First we need to define an annotation class
@Retention() @Target({, }) public @interface DebugTrace { }
Add this annotation where you need to insert the code, for example, in MainActivity:
public class MainActivity extends AppCompatActivity{ final String TAG = (); @Override protedcted void onCreate(Bundle savedInstanceState){ (savedInstanceState); setContentView(.activity_main); logTest(); } @DebugTrace public void logTest(){ (TAG,”log test"); } }
Finally create the code to cut
@Pointcut(“execution(@ * *..*.*(..))”) public void DebugTraceMethod(){} @Before(“DebugTraceMethod()”) public void beforeDebugTraceMethod(JoinPoint joinPoint) throws Throwable{ String key = ().toString(); (TAG, “beforeDebugTraceMethod:”+key); }
Call
In the point-cut expression of AspectJ, we used the execution before, and in fact there is 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 usually filters some point-cutting conditions and makes more precise entry control, as follows:
public class MainActivity extends AppCompatActivity{ final String TAG = (); @Orveride protected void onCreate(Bundle savedInstanceState){ (saveInstanceState); setContentView(.activity_main); aspectJ1(); aspectJ2(); aspectJ3(); } public void aspectJTest(){ (TAG,”execute aspectJTest"); } public void aspectJ1(){ aspectJTest(); } public void aspectJ2(){ aspectJTest(); } public void aspectJ3(){ aspectJTest(); } }
aspectJ1(), aspectJ2(), and aspectJ3() all call the aspectJTest methods, but you only want to insert the 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{ (TAG,”method:”+getMethodName(joinPoint).getName()); } private MethodSignature getMethodName(JoinPoint joinPoint){ if(joinPoint == null) return null; return (MethodSignature) (); }
execution syntax
execution() is the most commonly used point-cut function, and its syntax is as follows:
For example, the following syntax is: @Around("execution(* *..MainActivity+.on*(..))")
The entire expression can be divided into five parts:
() is the expression body
2. The first * represents the return type, and the * represents all types.
3. Package name indicates the package name that needs to be intercepted. * is used here to match all package names.
4. The second * number represents the class name, followed by .MainActivity refers to the specific class name called MainActivity.
5.*(..) The last asterisk represents the method name, +. represents the specific function name, the * wildcard character, including the parameters representing the method in the brackets, and the two dots represent any parameters.
Error encountered
1. The following error can be solved using gradle2.2.3, because it is not currently adapted 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
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.