Skip to content

AspectJ Migration Guide

Migrating from AspectJ is also very simple

1. Annotation-based aspects

AspectJ code

Take click annotation as an example, you may have such a code

Click annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {

    long DEFAULT_INTERVAL_MILLIS = 1000;

    /**
     * @return The interval between quick clicks (ms), the default is 1000ms
     */
    long value() default DEFAULT_INTERVAL_MILLIS;
}

Click annotation aspect

@Aspect
public final class SingleClick$$AspectJ {
    @Pointcut("within(@com.flyjingfish.light_aop_core.annotations.SingleClick *)")
    public final void withinAnnotatedClass() {
    }

    @Pointcut("execution(!synthetic * *(..)) && withinAnnotatedClass()")
    public final void methodInsideAnnotatedType() {
    }

    @Pointcut("execution(@com.flyjingfish.light_aop_core.annotations.SingleClick * *(..)) || methodInsideAnnotatedType()")
    public final void method() {
    }

    @Around("method() && @annotation(vSingleClick)")
    public final Object cutExecute(final ProceedingJoinPoint joinPoint,
                                   final SingleClick vSingleClick) {
// Section processing logic
        return result;
    }
}

AndroidAOP code

First create a class to handle sections

class SingleClickCut : ClickCut<SingleClick>() {
    //Fill in your original annotations for this pattern
    override fun invoke(joinPoint: ProceedJoinPoint, anno: SingleClick): Any? {
//Copy the logic code here and make some changes
        return null
    }

}

Then add the @AndroidAopPointCut(SingleClickCut.class) annotation on top of your original annotation. The annotation @Retention can only set RUNTIME, and @Target can only set METHOD

//Just add such an annotation. The parameter is the section processing class SingleClickCut.class created above
@AndroidAopPointCut(SingleClickCut.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {

    long DEFAULT_INTERVAL_MILLIS = 1000;

    /**
     * @return The interval between quick clicks (ms), the default is 1000ms
     */
    long value() default DEFAULT_INTERVAL_MILLIS;
}
Kotlin writing
@AndroidAopPointCut(SingleClickCut::class)
@Retention(AnnotationRetention.RUNTIME)
@Target(
    AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER
)
annotation class SingleClick(
    /**
     * The interval between quick clicks (ms), the default is 1000ms
     */
    val value: Long = DEFAULT_INTERVAL_MILLIS
) {
    companion object {
        const val DEFAULT_INTERVAL_MILLIS: Long = 1000
    }
}

2. Aspects that match the execution process of a class method

@AndroidAopMatchClassMethod is similar to the execution matching type in AspectJ, focusing on the execution of methods

AndroidAOP currently only matches methods of a class, not just a method regardless of the class. Because the author thinks that doing so is almost meaningless, and doing so often leads to the addition of many classes that do not want to be added to the aspect, which is not conducive to everyone's management and control of their own code (a bit out of control~)

AspectJ code

For example, you originally set the aspect code for threadTest of MainActivity, as shown below:

package com.flyjingfish.test

class MainActivity : BaseActivity() {
    fun threadTest() {
        Log.e("threadTest", "------")
    }
}

The matching aspect code of AspectJ is as follows:

@Aspect
public class CheckAspectJ {
    private static final String TAG = "CheckAspectJ";

    @Pointcut("execution(* com.flyjingfish.test.MainActivity.threadTest())")
    public void pointcutThreadTest() {
    }

    @Around("pointcutThreadTest()")
    public void calculateFunctionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        Log.i(TAG, "pointcut1 ---------calculateFunctionTime---------@Around");
        long beginTime = System.currentTimeMillis();
        joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        Log.i(TAG, "pointcut1 ----------calculateFunctionTime-- -----Running time: " + (endTime - beginTime));
    }
} 

AndroidAOP code@AndroidAopMatchClassMethod

@AndroidAopMatchClassMethod(
    targetClassName = "com.flyjingfish.test.MainActivity",
    methodName = ["threadTest"],
    type = MatchType.SELF
)
class MatchActivityMethod : MatchClassMethod {
    override fun invoke(joinPoint: ProceedJoinPoint, methodName: String): Any? {
        Log.e("MatchActivityMethod", "=====invoke=====$methodName")
        long beginTime = System . currentTimeMillis ();
        joinPoint.proceed();
        long endTime = System . currentTimeMillis ();
        return null
    }
}

3. Match the called aspect of a class method

@AndroidAopReplaceClass is similar to the call matching type in AspectJ, focusing on the method call

AspectJ code

Those who have used AspectJ should know that some system methods can only be matched through call. For example, you originally targeted e of android.util.Log sets the aspect code, and the matching aspect code of AspectJ is as follows:

@Aspect
public final class TestAspectJ {
    @Pointcut("call(* android.util.Log.e(..))")
    public void pointcutThreadTest() {
    }

    @Around("pointcutThreadTest()")
    public final Object cutExecute(final JoinPoint joinPoint) throws Throwable {
        Log.e("TestAspectJ", "====cutExecute");
        return null;
    }
}

AndroidAOP code

Click here to see detailed usage of @AndroidAopReplaceClass

@AndroidAopReplaceClass("android.util.Log")
object ReplaceLog {
    @AndroidAopReplaceMethod("int e(java.lang.String tag, java.lang.String msg)")
    @JvmStatic
    fun logE(String tag, String msg): Int {
        return Log.e(tag, msg)
    }
} 

Note

Unlike AspectJ, AndroidAOP does not retain the way to execute the original method, but you can call the original method yourself without causing infinite recursive calls (indirect calls to the original method will cause infinite recursion here is a solution), Click here for detailed usage

4. Other aspect methods

  • @Before: Execute code before method execution.
  • @After: Execute code after method execution, regardless of whether an exception is thrown.
  • @AfterReturning: Execute code after method execution, only when the method returns successfully.
  • @AfterThrowing: Execute code when a method throws an exception.

All of the above can be indirectly implemented through several existing annotation aspects click here to refer to FAQ #5 Want to insert code before and after the method