Personal blog

www.milovetingting.cn

Simple implementation of Android buried point scheme – AspectJ of AOP

The definition of AOP

AOP for the abbreviation of Aspect Oriented Programming, meaning: section-oriented Programming, through pre-compilation and runtime dynamic proxy to achieve unified maintenance of program functions of a technology.

The above definition of AOP is quoted from Baidu Encyclopedia.

AOP application scenarios

Logs, performance statistics, permission control, and buried points

There are many concrete implementation schemes of AOP, and AspectJ is chosen to achieve it simply

  1. Listen to the View click, the page open and close
  2. Add start and end logs for methods
  3. Statistics method run time

The use of AspectJ

The introduction of the AspectJ

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

Create an Android project and add dependencies to the build.gradle file in the root directory of the project

dependencies {
        / /...
        classpath 'com. Hujiang. Aspectjx: gradle - android plugin - aspectjx: mid-atlantic moved'
        / /...
    }
Copy the code

Create a new Module, select Android Library as the type, and add the corresponding dependencies to the build.gradle file of the new Library

apply plugin: 'android-aspectjx'
Copy the code

Add a reference to the library you just created and a dependency on AspectJ to your app’s build.gradle file

apply plugin: 'android-aspectjx'

dependencies {
    / /...
    implementation project(':library')}Copy the code

Listen to the View click, the page open and close

Create a new callback interface in the Library, TrackCallBack

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);

}
Copy the code

Create a pointcut TrackPoint in the Library

public class TrackPoint {

    private static TrackCallBack mTrackCallBack;

    private TrackPoint(a) {}/** * initialize *@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); }}Copy the code

Create a new section TraceAspect in the library

@Aspect
public class TraceAspect {

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

    @Pointcut("execution(* onClick(..) )")
    public void onClickPointcut(a) {}@Pointcut("execution(* android.app.Activity+.onCreate(..) )")
    public void activityOnCreatePointcut(a) {}@Pointcut("execution(* android.app.Activity+.onDestroy(..) )")
    public void activityDestroyPointcut(a) {}@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(); }}Copy the code

Create a new Application in the app module and initialize it in onCreate:

public class App extends Application {

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

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

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

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

New applications need to be referenced in AndroidManifest to take effect.

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

The 2020-01-13 16:50:17. 373, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: OnPageOpen: com. Wangyz. Aspectjdemo. MainActivity 16:50:19. 2020-01-13, 243, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: OnClick: com. Wangyz. Aspectjdemo. MainActivity - btn_open 16:50:19 2020-01-13. 298, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: OnPageOpen: com. Wangyz. Aspectjdemo. SecondActivity 16:50:21. 2020-01-13, 392, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: OnPageClose: com. Wangyz. Aspectjdemo. SecondActivity 16:50:22. 2020-01-13, 320, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.MainActivityCopy the code

Add start and end logs for methods

Add the annotation AddLog to the library

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

Add the following code to TraceAspect

@Pointcut("execution(@com.wangyz.library.AddLog * *(..) )")
    public void addLogPointcut(a) {}@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(); }}Copy the code

Add the AddLog annotation to MainActivity’s onCreate

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

After the App is run, the following logs are generated:

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
Copy the code

Statistics method run time

Add the annotation ExecTime to the library

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

Add the following code to TraceAspect

@Pointcut("execution(@com.wangyz.library.ExecTime * *(..) )")
    public void execTimePointcut(a) {}@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(); }}Copy the code

Add ExecTime annotations to the 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; }}Copy the code

After the App is run, the following logs are generated:

The 2020-01-13 16:50:19. 272, 16610-16610 / com. Wangyz. Aspectjdemo D/TraceAspect: execute time:com.wangyz.aspectjdemo.MainActivity-onClick : 28msCopy the code

Source code address :github.com/milovetingt…