Android AspectJ 常用埋点,拦截,监听注解使用

做一个学习记录:

Android中的AOP编程
Android之AOP
Android Studio 中自定义 Gradle 插件
看AspectJ在Android中的强势插入
jarryleo / MagicBuriedPoint

AspectJ 的两种用法

(1)用自定义注解修饰切入点,精确控制切入点,属于侵入式;

(2)不需要在切入点代码中做任何修改,属于非侵入式。

侵入式

侵入式用法,一般会使用自定义注解,以此作为选择切入点的规则。

非侵入式

非侵入式,就是不需要使用额外的注解来修饰切入点,不用修改切入点的代码。

项目接入使用:

一般情况下我们会新建一个独立模块提供给其它项目接入使用,如下图TrackPoint模块

Android AspectJ 常用埋点,拦截,监听注解使用

build.gradle模块配置

两种方式:1.apply plugin: 'android-aspectjx'

                  2.implementation 'org.aspectj:aspectjrt:1.8.+'

Android AspectJ 常用埋点,拦截,监听注解使用

注解方式:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AspectAnalyze {
    String name();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AspectDebugLog {
}
public class MainActivity extends Activity {
    private Button myButton;
    private final String TAG= this.getClass().getSimpleName();

    @AspectAnalyze(name = "MainActivity.onCreate")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myButton=findViewById(R.id.myButton);
        myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this,"text",Toast.LENGTH_LONG).show();
                onNameClick();
            }
        });
    }

    @AspectDebugLog
    @AspectAnalyze(name = "onNameClick")
    public void onNameClick() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @AspectAnalyze(name = "MainActivity.onDestroy")
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
    }
}
package com.trackpoint;

import android.util.Log;

import com.trackpoint.annotation.AspectAnalyze;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;

@Aspect
public class AnnotationAspectTrace {
    private final String TAG = this.getClass().getSimpleName();
    private static AspectTraceListener aspectTraceListener;

    /**
     * 针对所有继承 Activity 类的 onCreate 方法
     */
    @Pointcut("execution(* android.app.Activity+.onCreate(..))")
    public void activityOnCreatePointcut() {

    }

    /**
     * 针对带有AspectAnalyze注解的方法
     */
    @Pointcut("execution(@com.trackpoint.annotation.AspectAnalyze * *(..))")
    public void aspectAnalyzeAnnotation() {
    }
    /**
     * 针对带有AspectAnalyze注解的方法
     */
    @Pointcut("execution(@com.trackpoint.annotation.AspectDebugLog * *(..))")
    public void aspectDebugLogAnnotation() {
    }
    /**
     * 针对前面 aspectAnalyzeAnnotation() 的配置
     */
    @Around("aspectAnalyzeAnnotation()")
    public void aroundJoinAspectAnalyze(final ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        AspectAnalyze aspectAnalyze = methodSignature.getMethod().getAnnotation(AspectAnalyze.class);
        long startTimeMillis = System.currentTimeMillis();
        joinPoint.proceed();
        if (aspectTraceListener != null) {
            aspectTraceListener.onAspectAnalyze(joinPoint, aspectAnalyze, methodSignature, System.currentTimeMillis() - startTimeMillis);
        }
    }
    /**
     * 针对前面 aspectDebugLogAnnotation() 或 activityOnCreatePointcut() 的配置
     */
    @Around("aspectDebugLogAnnotation() || activityOnCreatePointcut()")
    public void aroundJoinAspectDebugLog(final ProceedingJoinPoint joinPoint) throws Throwable {
        long startTimeMillis = System.currentTimeMillis();
        joinPoint.proceed();
        long duration = System.currentTimeMillis() - startTimeMillis;
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        SourceLocation location = joinPoint.getSourceLocation();
        String message = String.format("%s(%s:%s) [%sms]", methodSignature.getMethod().getName(), location.getFileName(), location.getLine(), duration);
        if (aspectTraceListener != null) {
            aspectTraceListener.logger("AspectTrace", message);
        } else {
            Log.e("AspectTrace", message);
        }
    }

    public static void setAspectTraceListener(AspectTraceListener aspectTraceListener) {
        AnnotationAspectTrace.aspectTraceListener = aspectTraceListener;
    }

    public interface AspectTraceListener {
        void logger(String tag, String message);

        void onAspectAnalyze(ProceedingJoinPoint joinPoint, AspectAnalyze aspectAnalyze, MethodSignature methodSignature, long duration);
    }
}

非侵入式:

以下几个常用场景

package com.trackpoint;

import android.util.Log;
import android.widget.Toast;

import com.trackpoint.annotation.AspectAnalyze;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;

@SuppressWarnings("unused")
@Aspect
public class AspectTrace {
    private final String TAG = "AspectTrace";

    @Around("call(* android.widget.Toast.setText(java.lang.CharSequence))")
    public void handleToastText(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Log.d(TAG," start handleToastText");
        proceedingJoinPoint.proceed(new Object[]{"处理过的toast"}); //这里把它的参数换了
        Log.d(TAG," end handleToastText");
    }

    @Before("call(* android.widget.Toast.show())")
    public void changeToast(JoinPoint joinPoint) throws Throwable {
        Toast toast = (Toast) joinPoint.getTarget();
        toast.setText("修改后的toast");
        Log.d(TAG, " --> changeToast");
    }

    /**
     * 在MainActivity的所有生命周期的方法中打印log
     * @param joinPoint
     * @throws Throwable
     */
    @Before("execution(* android.app.Activity.**(..))")
    public void method(JoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = joinPoint.getThis().getClass().getSimpleName();
        Log.e(TAG, "class:" + className+" method:" + methodSignature.getName());
    }

}

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

    @Pointcut("execution(void android.view.View.OnClickListener.onClick(..))")
    public void onClickPointcut() {
    }

    @Pointcut("execution(* *.*onTouch(..))")
    public void onTouchPointcut() {
        Log.d(TAG,"onTouchPointcut");
    }


    @Around("onClickPointcut()")
    public void aroundJoinClickPoint(final ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = "";
        if (target != null) {
            className = target.getClass().getName();
        }
        //获取点击事件view对象及名称,可以对不同按钮的点击事件进行统计
        Object[] args = joinPoint.getArgs();
        if (args.length >= 1 && args[0] instanceof View) {
            View view = (View) args[0];
            int id = view.getId();
            String entryName = view.getResources().getResourceEntryName(id);
            TrackPoint.onClick(className, entryName);
        }
        joinPoint.proceed();//执行原来的代码
    }
}

遇到问题:

1.使用过程中经常对不上接口onTouch(..)

2.每次改变aspect代码需要clean项目

github demo代码:https://github.com/xingchongzhu/AspectJDemo

上一篇:Spring AOP之AspectJ实现方式


下一篇:Spring AOP 基本概念和使用