Simple use of AspectJ for Android AOP

1. The difference between AOP and OOP

In the project, there are many places that need to verify and log in, and many repetitive codes will be written. For this type of problem, AOP can be used to deal with it. First, understand the difference between AOP and OOP.

OOP:

Terminology: OOP (Object-Oriented Programming) abstracts and encapsulates entities and their attributes and behaviors in the business process to obtain clearer and more efficient division of logical units.
Object-oriented focuses on static, noun, state, organization, data, and the carrier is space;

Vernacular: The three major characteristics of OOP object-oriented: encapsulation , inheritance , and polymorphism . These characteristics also show that OOP is object-oriented. We consider an object in everything we do. When we need to complete a task, we generally want to encapsulate some operations into a class. All variables and operations are encapsulated into a class. , then this class is our object. If we want to achieve a specific function, we must first think about implementing it in this object.
Object-oriented also has obvious disadvantages. For example, if we want to implement some functions that are not commonly used, we need to implement these functions one by one in the required objects, and we must continue to maintain these functions. tired.
For example, some button statistics, life cycle statistics, and specific statistics in Android are relatively trivial things. It is not perfect to use object-oriented thinking to realize them. This requires implementing them one by one, which seems very trivial.

AOP:

Technical terms: AOP is to extract aspects in the business processing process. It is facing a certain step or stage in the processing process, so as to obtain the isolation effect of low coupling between various parts in the logic process.

Vernacular: AOP is aspect-oriented. When I use it, I focus on the specific methods and function entry points. I don’t need to know or care about the class or object. We only focus on the realization of the function, and it doesn’t matter who the specific object is.

If we say that if OOP is to divide the problem into a single module, then AOP is to manage a certain type of problem involving many modules in a unified manner.

Two AOP is how to achieve?

Java implementation of AOP

The function points of the method that AOP focuses on do not know who the object is in advance. Of course, the operation of the program needs to get the object to run. To get the object and execute it on the premise of knowing the function point of the method , this requires the use of Java. dynamic proxy .
In java's dynamic proxy mechanism, there are two important classes or interfaces, one is InvocationHandler (Interface) and the other is Proxy (Class). We can write code to define our own dynamic proxy to implement AOP, but Too much trouble. Java has many AOP implementation libraries, such as JBoss, SpringFramework, AspectJ, etc. in JavaWeb. The libraries based on AOP in Android include ButterKnife, dagger2, EventBus3.0, Retrofit 2.0, etc. It does not mean that these libraries are completely based on AOP, but that some of the functions in them are based on AOP.

Java Annotation

Java Annotation is an annotation mechanism introduced by JDK5.0.

Of course, we can also customize annotations, but our custom annotations require us to reflect or dynamic proxy to process the annotations once to achieve AOP. It is more troublesome to process by ourselves, and reflection is also more performance-consuming. It is not recommended to use it. It is best Annotation+APT is used to translate annotations into Java code to avoid performance loss.

AnnotatedElement

Java's Class class has a series of implementations that support Annotation and reflection. There is a key interface: AnnotatedElement, which contains a series of methods that support Annotation during Class reflection.

We can implement our own AOP based on the Target and Retention in java-based Annotation combined with similar reflection principles. Many Android AOP implementations do the same.

The object of this interface (AnnotatedElement) represents an "annotated element" (which can be Class, Method, Field, Constructor, Package, etc.) in the current JVM.

In the Java language, all "elements" that implement this interface are "annotated elements". The annotations of "annotated elements" can be read (via Java's reflection mechanism) using the methods declared in this interface. All methods in this interface return annotations that are immutable and serializable. The arrays returned by all methods in this interface can be modified by the caller without affecting the arrays returned to other callers.

Android implementation of AOP

Android is written in Java. The Java Annotation dynamic proxy mentioned above is of course applicable to Android, but Android also has its own AOP implementation method, but the AOP implementation principle of Android is the same as that of Java.

There are also Google's official custom AndroidAnnotations in the Android source code

According to the packaging process of apk and as shown in the figure below, Android has different methods to implement AOP at different stages

Packaging process of apk

  1. Pack resource files and generate R.java files
  2. Process aidl files and generate Java files
  3. Compile the project source code to generate a class file
  4. Generate all the calss files into the classes.dex file
  5. Package and generate APK file
  6. Sign the APK
  7. Process the signed APK file.
    insert image description here

1)APT

APT overview

The annotation processing tool APT (Annotation Processing Tool) can process annotations at compile time .

APT is used to scan and process annotation information in the source code during compilation. We can generate some files, such as Java files, according to the annotation information . Use the Java code generated by APT to realize redundant code functions, which reduces manual code input, improves coding efficiency, and makes the source code look clearer and more concise.

Since Java5, JDK has built-in annotation processor APT, but APT has become really popular in recent years, thanks to the fact that various mainstream libraries on Android are implemented using APT, such as Dagger, ButterKnife, AndroidAnnotation, EventBus, etc.

2)AspectJ

Introduction
  1. AspectJ is a code generation tool (Code Generator).
  2. AspectJ syntax is the syntax used to define code generation rules.

AspectJ is an aspect-oriented framework that extends the Java language. AspectJ defines the AOP grammar, which has a special compiler to generate Class files that comply with the Java byte encoding specification, and supports static compilation and dynamic compilation.

  • Advantages: can be woven into all classes; support compile-time and load-time code injection; simple to write, powerful.
  • Disadvantages: It needs to be compiled with the ajc compiler, which is an extension of the java compiler and has all its functions.
scenes to be used
  • Performance monitoring: record the call time before and after the method call, and alarm if the method execution is too long or timed out.
  • Traceless burying: Add corresponding statistical codes where burying is required.
  • Cache proxy: Cache the return value of a method, and get it directly from the cache the next time the method is executed.
  • Logging: Record system logs before and after method execution.
  • Authorization verification: Before the method is executed, it is verified whether there is authorization to execute the current method. If not, an execution exception without authorization is thrown, which is caught by the business code.
  • other

AspectJ syntax

Here are just some simple concepts. If you want to know more about usage, you can check the official website.

1. Aspect

An aspect is an independent function implementation. A program can define multiple aspects. To define an aspect, you need to create a new class and add the @Aspect annotation.

2. JointPoint (link point)

The link point represents the position where the code can be cut in the program, including function call and execution, class initialization, exception handling, etc. The link point is the place where AspectJ can be used to intrude and modify. For example, the call and execution of the onCreate method in the Activity, the initialization of the Activity, the execution of the construction method, etc., you can cut into the code you want at these JointPoints (link points).

Selectable JointPoint in AspectJ

JoinPoint illustrate example
method call function call For example calling Log.e( )
method execution function execution For example, inside the execution of Log.e( ).
method call is the place where a function is called
execution is the internal execution of a 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 MainActivity.mTest member
field set set a variable For example, set the MainActivity.mTest member
pre-initialization Object does some work in the constructor
initialization What Object does in the constructor
static initialization class initialization For example, the static {} of the class
handler exception handling For example, in try catch(xxx), it corresponds to the execution in the catch
advice execution Contents of AspectJ
3. PointCut (cut point)

The pointcut is a specific link point, which defines the link point that needs to be woven into the code, and the definition of the pointcut has a special syntax.

An expression that tells the code injection tool where to inject a specific piece of code.

execution : handle the type of Join Point, such as call, execution, withincode

Among them, call and execution are similar, and both mean to insert code. The difference is that execution is in the cut-in method, and call is before or after calling the cut-in method.

The syntax of withcode is usually used to filter some entry point conditions for more precise entry control

MethodPattern

This is the most important expression, which is roughly: @Annotation access permission type package name. function name (parameter )

@ annotations and access permissions (public/private/protect, and static/final) are optional. If you don't set them, both are selected by default. Taking access rights as an example, if access rights are not set as a condition, public, private, protected, static, and final functions will all be searched.

**Return value type:** is the return value type of an ordinary function. If the type is not limited, use the * wildcard to indicate

PackageName.FunctionName is used to find matching functions. Wildcards can be used, including and ... and the + sign. Among them, the sign is used to match any character except the sign, and ... means any sub-package, and the + sign means a sub-category.

Let's analyze the matching in the example below

* execution(*com.hengda.dsp.aopjavademo.MainActivity.test*(..))
1

The first part: "" means the return value, "" means the return value is of any type,
the second part: it is a typical package name path, which can contain "" for wildcarding, and there is no difference between several "". At the same time, here you can use "&&, ||, !" to combine conditions.
Similar to [test*], it means any method starting with test as the method name.
The third part: () 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 can also be written in a mixed way (android.os.Bundle, ...) to represent that the first parameter is a bundle, and the rest is optional.

4. Advice

Notification represents the monitoring of the pointcut, and the code that needs to be woven can be written here. Notifications include: before the execution of the @Before method, after the execution of the @After method, before and after the execution of the @Around method. For example: the following shows the monitoring before, after, and before and after the execution of the point-cutting activityOnCreate, where @Around needs to handle the execution of the method by itself, and must be placed before @Before and @After.

Advice is how the code we insert is inserted, including Before, After, and Around.

Key words illustrate
Before Execute before the entry point . Before the entry point is executed, we can execute our method first, which can intercept the execution of the entry point, such as intercepting operations that require user payment or login
after Execute after the entry point , such as the operation of recording user behavior
around Before and after the pointcut is included, the execution of the pointcut can be controlled . For example, conditional judgment is required before execution, and the operation that prompts the user after execution is completed
AfterReturning When the connection point method is successfully executed, the return notification method will be executed . If an exception occurs in the connection point method, the return notification method will not be executed. The return notification method will be executed after the target method is successfully executed, so the return notification method can get the result after the target method (join point method) is executed.
AfterThrowing The exception notification method will only be executed after an exception occurs in the connection point method , otherwise it will not be executed.

We can use the JoinPoint parameter to get more content:

  • java.lang.Object[] getArgs(): Get the input parameter list when the connection point method is running;
  • Signature getSignature() : Get the method signature object of the connection point;
  • java.lang.Object getTarget() : Get the target object where the connection point is located;
  • java.lang.Object getThis() : Get the proxy object itself;

3)Javassist

Javassist is an open source library for analyzing, editing, and creating Java bytecode.
Created by Shigeru Chiba of the Department of Mathematics and Computer Science, Tokyo Institute of Technology . It has joined the open source JBoss
application server project to implement a dynamic "AOP" framework for JBoss by manipulating bytecode using Javassist. - Baidu Encyclopedia

Representative framework: hotfix framework HotFix, Savior (InstantRun), etc.

3. Using AspectJ in Android projects

Add dependencies to the build.gradle file in the root directory of the project

 classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'

Then add in the build.gradle file of the project or library

apply plugin: 'android-aspectjx'


Create an AspectTest class using

@Aspect
public class AspectTest {
    
    
    final String TAG = AspectTest.class.getSimpleName();

    @Before("execution(* *..MainActivity+.on**(..))")
    public void method(JoinPoint joinPoint) throws Throwable {
    
    
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getThis().getClass().getSimpleName();

        Log.e(TAG, "class:" + className);
        Log.e(TAG, "method:" + methodSignature.getName());
    }
}

The code of MainActivty is as follows

public class MainActivity extends AppCompatActivity {
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

The log is as follows

insert image description here

Login verification implementation

// 用户登录检测
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginCheck {
    
    
}

@Aspect // 定义切面类
public class LoginCheckAspect {
    
    

    private final static String TAG = "TAG";

    // 1、应用中用到了哪些注解,放到当前的切入点进行处理(找到需要处理的切入点)
    // execution,以方法执行时作为切点,触发Aspect类
    // * *(..)) 可以处理ClickBehavior这个类所有的方法
    @Pointcut("execution(@com.hengda.dsp.aopjavademo.annotation.LoginCheck * *(..))")
    public void methodPointCut() {
    
    }

    // 2、对切入点如何处理
    @Around("methodPointCut()")
    public Object jointPotin(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        Context context = (Context) joinPoint.getThis();
        if (false) {
    
     // 从SharedPreferences中读取
            Log.e(TAG, "检测到已登录!");
            return joinPoint.proceed();
        } else {
    
    
            Log.e(TAG, "检测到未登录!");
            Toast.makeText(context, "请先登录!", Toast.LENGTH_SHORT).show();
            return null; // 不再执行方法(切入点)
        }
    }
}

Finally, add the annotation @LoginCheck where the login needs to be verified

Guess you like

Origin blog.csdn.net/zhangshiwen11/article/details/120150366
Recommended