Simple use of AspectJ in AOP

Keywords: Android Gradle Programming github

Personal blog

http://www.milovetingting.cn

Simple use of AspectJ in AOP

Definition of AOP

AOP is the abbreviation of Aspect Oriented Programming, which means: a technology to realize the unified maintenance of program functions through precompiling and dynamic agent during operation.

The above definition of AOP is quoted from Baidu Encyclopedia.

AOP application scenarios

Log record, performance statistics, authority control, burying point, etc

There are many specific implementation schemes of AOP. Here, AspectJ is selected for simple implementation

  1. Monitor the click, page open and close of View
  2. Add start and end logs for methods
  3. Running time of statistical method

Use of AspectJ

Introduction of AspectJ

AspectJX is referenced here. AspectJX is an AOP framework based on AspectJ

Create a new Android project and add a dependency to the build.gradle file in the root directory of the project

dependencies {
        //...
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
        //...
    }

Create a new Module, select Android Library as the type, and add the corresponding dependency in the build.gradle file of the new library

apply plugin: 'android-aspectjx'

Add reference to the newly created library and AspectJ dependency in the build.gradle file of app

apply plugin: 'android-aspectjx'

dependencies {
    //...
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

Monitor the click, page open and close of View

Create a new callback interface TrackCallBack in library

public interface TrackCallBack {

    /**
     * When View is clicked
     *
     * @param pageName
     * @param viewIdName
     */
    void onClick(String pageName, String viewIdName);

    /**
     * When the page opens
     *
     * @param pageName
     */
    void onPageOpen(String pageName);

    /**
     * When the page is closed
     *
     * @param pageName
     */
    void onPageClose(String pageName);

}

Create a new pointpointpointtrackpoint in the library

public class TrackPoint {

    private static TrackCallBack mTrackCallBack;

    private TrackPoint() {

    }

    /**
     * Initialization
     * @param trackCallBack
     */
    public static void init(TrackCallBack trackCallBack) {
        mTrackCallBack = trackCallBack;
    }

    static void onClick(String pageName, String viewIdName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onClick(pageName, viewIdName);
    }

    static void onPageOpen(String pageName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onPageOpen(pageName);
    }

    static void onPageClose(String pageName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onPageClose(pageName);
    }

}

Create a new facet tracespect in the library

@Aspect
public class TraceAspect {

    private static final String TAG = TraceAspect.class.getSimpleName();

    @Pointcut("execution(* onClick(..))")
    public void onClickPointcut() {

    }

    @Pointcut("execution(* android.app.Activity+.onCreate(..))")
    public void activityOnCreatePointcut() {

    }

    @Pointcut("execution(* android.app.Activity+.onDestroy(..))")
    public void activityDestroyPointcut() {

    }

    @Around("onClickPointcut()")
    public void onClick(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = "";
        if (target != null) {
            className = target.getClass().getName();
        }
        Object[] args = joinPoint.getArgs();
        if (args.length > 0 && args[0] instanceof View) {
            View view = (View) args[0];
            String entryName = view.getResources().getResourceEntryName(view.getId());
            TrackPoint.onClick(className, entryName);
        }
        joinPoint.proceed();
    }

    @Around("activityOnCreatePointcut()")
    public void pageOpen(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = target.getClass().getName();
        TrackPoint.onPageOpen(className);
        joinPoint.proceed();
    }

    @Around("activityDestroyPointcut()")
    public void pageClose(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = target.getClass().getName();
        TrackPoint.onPageClose(className);
        joinPoint.proceed();
    }

}

Create a new Application in app module and perform initialization in onCreate:

public class App extends Application {

    private static final String TAG = TraceAspect.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        TrackPoint.init(new TrackCallBack() {
            @Override
            public void onClick(String pageName, String viewIdName) {
                Log.d(TAG, "onClick:" + pageName + "-" + viewIdName);
                //Execute corresponding business
            }

            @Override
            public void onPageOpen(String pageName) {
                Log.d(TAG, "onPageOpen:" + pageName);
                //Execute corresponding business
            }

            @Override
            public void onPageClose(String pageName) {
                Log.d(TAG, "onPageClose:" + pageName);
                //Execute corresponding business
            }
        });
    }
}

The new Application needs to be referenced in Android manifest for it to take effect.

After running the App, click to open another Activity, and then exit the Activity in turn. The output log is as follows:

2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.MainActivity
2020-01-13 16:50:19.243 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onClick:com.wangyz.aspectjdemo.MainActivity-btn_open
2020-01-13 16:50:19.298 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:21.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:22.320 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.MainActivity

Add start and end logs for methods

Add the annotation AddLog to the library

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

Add the following code to tracespec

@Pointcut("execution(@com.wangyz.library.AddLog * *(..))")
    public void addLogPointcut() {

    }

@Around("addLogPointcut()")
    public void addLog(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AddLog addLog = signature.getMethod().getAnnotation(AddLog.class);
        if (addLog != null) {
            Object target = joinPoint.getTarget();
            String className = "";
            if (target != null) {
                className = target.getClass().getName();
            }
            Log.d(TAG, "start execute:" + className + "-" + signature.getMethod().getName());
            joinPoint.proceed();
            Log.d(TAG, "end execute:" + className + "-" + signature.getMethod().getName());
        } else {
            joinPoint.proceed();
        }
    }

Add AddLog annotation on onCreate of MainActivity

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

After running the App, enter the log as follows:

2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: start execute:com.wangyz.aspectjdemo.MainActivity-onCreate
2020-01-13 16:50:17.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: end execute:com.wangyz.aspectjdemo.MainActivity-onCreate

Running time of statistical method

Add comment ExecTime to library

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

Add the following code to tracespec

@Pointcut("execution(@com.wangyz.library.ExecTime * *(..))")
    public void execTimePointcut() {

    }

@Around("execTimePointcut()")
    public void execTime(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        ExecTime execTime = signature.getMethod().getAnnotation(ExecTime.class);
        if (execTime != null) {
            long start = System.currentTimeMillis();
            joinPoint.proceed();
            long end = System.currentTimeMillis();
            Object target = joinPoint.getTarget();
            String className = "";
            if (target != null) {
                className = target.getClass().getName();
            }
            Log.d(TAG,
                    "execute time:" + className + "-" + signature.getMethod().getName() + " : " + (end - start) + "ms");
        } else {
            joinPoint.proceed();
        }
    }

Add ExecTime annotation to onClick method

@ExecTime
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_open:
                Intent intent = new Intent(this, SecondActivity.class);
                startActivity(intent);
                break;
            default:
                break;
        }
    }

After running the App, the output log is as follows:

2020-01-13 16:50:19.272 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: execute time:com.wangyz.aspectjdemo.MainActivity-onClick : 28ms

Source address: https://github.com/milovetingting/Samples/tree/master/AspectJDemo

Posted by Delcypher on Mon, 13 Jan 2020 02:43:50 -0800