当前位置:   article > 正文

框架手写系列---AspectJ方式实现埋点上传框架_android aspectj 可视化埋点上报

android aspectj 可视化埋点上报

一、切面编程

AOP、OOP是程序中经常涉及的概念。

OOP--面向对象编程,java、c#等都是面向对象的编程语言,主张万物皆对象。

AOP是一种编程思想,对OOP的有效补充,在具体代码中,可以针对任意方法、属性、类等,做一个切面处理。针对被标记(注解标记)的代码,做额外处理。

AspectJ是AOP思想的实现方式,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

二、埋点上传

AspectJ埋点上传的原理:在需要埋点的地方,用注解方式,标注一个切入点。后续在@Aspect注解的类中,处理该切入点,处理完成后,将代码织入到原代码中。

@Aspect注解的类,将使用特定的编译器ajc编译,而不再是javac编译。

1、依赖引用

1-1:在最外层的build中,新增以下依赖:

  1. dependencies {
  2. classpath 'com.android.tools.build:gradle:3.6.3'
  3. //以下为新增依赖
  4. classpath 'org.aspectj:aspectjtools:1.9.1'
  5. classpath 'org.aspectj:aspectjweaver:1.9.1'
  6. // NOTE: Do not place your application dependencies here; they belong
  7. // in the individual module build.gradle files
  8. }

1-2:在具体工程的build中,新增以下依赖,以下依赖主要用于日志打印。

此处需注意,根据模块的不同,遍历使用不同的属性:

app模块使用:applicationVariants
library模块使用:libraryVariants

  1. import org.aspectj.bridge.IMessage
  2. import org.aspectj.bridge.MessageHandler
  3. import org.aspectj.tools.ajc.Main
  4. def log = project.logger
  5. //app模块使用:applicationVariants
  6. //library模块使用:libraryVariants
  7. android.applicationVariants.all { variant ->
  8. if (!variant.buildType.isDebuggable()) {
  9. log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
  10. return
  11. }
  12. JavaCompile javaCompile = variant.javaCompile
  13. javaCompile.doLast {
  14. String[] args = ["-showWeaveInfo",
  15. "-1.5",
  16. "-inpath", javaCompile.destinationDir.toString(),
  17. "-aspectpath", javaCompile.classpath.asPath,
  18. "-d", javaCompile.destinationDir.toString(),
  19. "-classpath", javaCompile.classpath.asPath,
  20. "-bootclasspath", android.bootClasspath.join(
  21. File.pathSeparator)]
  22. MessageHandler handler = new MessageHandler(true);
  23. new Main().run(args, handler)
  24. for (IMessage message : handler.getMessages(null, true)) {
  25. switch (message.getKind()) {
  26. case IMessage.ABORT:
  27. case IMessage.ERROR:
  28. case IMessage.FAIL:
  29. log.error message.message, message.thrown
  30. break;
  31. case IMessage.WARNING:
  32. case IMessage.INFO:
  33. log.info message.message, message.thrown
  34. break;
  35. case IMessage.DEBUG:
  36. log.debug message.message, message.thrown
  37. break;
  38. }
  39. }
  40. }
  41. }

2、定义埋点上传需要的注解

本例中共定义了三种注解,分别用于:修饰注解的基础注解、定义切入点注解、参数注解

2-1:BasePoint

该注解,用于埋点的分类。实际埋点可能分为多种类型,如异常埋点、页面进入埋点、点击埋点等。type代表类别名称,typeId代表类别ID。

  1. @Target(ElementType.ANNOTATION_TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface BasePoint {
  4. String type();
  5. String typeId();
  6. }

2-2:DataPoint

该注解是实际用于切点的埋点,让aspectJ识别。BasePoint也是修饰该注解(避免在实际应用中,需要多次手写type与typeId)。

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @BasePoint(type = "数据埋点",typeId = "1001")
  4. public @interface DataPoint {
  5. String value() default "";
  6. }

2-3:Parameter

该注解用于方法参数的注解。埋点时具体上送的内容,通过该参数注解与参数值,组成key-value形式上送

  1. @Target(ElementType.PARAMETER)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Parameter {
  4. String value();
  5. }

 

3、AspectJ实现方法切面管理

3-1:在具体需要埋点的方法中,在方法上以及方法的参数中加上注解

  1. @DataPoint
  2. private static void testPointStaticPrivate(@Parameter("key1") int a, String b) {
  3. Log.e("MainActivity", "testPointStaticPrivate: "+"方法被执行" );
  4. }

3-2:新建类,用Aspect注解该类。在该类中收集数据并上传

  1. @Aspect
  2. public class AspectManager {
  3. private static final String TAG = "AspectManager";
  4. @Pointcut("execution(@com.sunny.aaspect.annotation.DataPoint * *(..))")
  5. public void uploadDataPoint() {
  6. }
  7. @Around("uploadDataPoint()")
  8. public Object handleUploadPoint(ProceedingJoinPoint joinPoint) {
  9. //获取到方法的反射对象
  10. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  11. //方法实体
  12. Method method = signature.getMethod();
  13. //方法上的所有注解
  14. Annotation[] annotations = method.getAnnotations();
  15. //获取方法的接收参数的注解
  16. Annotation[] methodParamsAnnotations = getMethodParamsAnnotations(method);
  17. BasePoint basePoint = null;
  18. for (Annotation annotation : annotations) {
  19. basePoint = annotation.annotationType().getAnnotation(BasePoint.class);
  20. if (basePoint == null) {
  21. break;
  22. }
  23. }
  24. if (basePoint == null) {
  25. return sourceMethod(joinPoint);
  26. }
  27. //获取到基础注解上的类别和类别ID
  28. String type = basePoint.type();
  29. String typeId = basePoint.typeId();
  30. //获取到参数上的key-value
  31. JSONObject paramsData = getParamsData(methodParamsAnnotations, joinPoint.getArgs());
  32. Log.e(TAG, "上传数据----> type:" + type + " typeId:" + typeId + " params:" + (paramsData != null ? paramsData.toJSONString() : ""));
  33. return sourceMethod(joinPoint);
  34. }
  35. private JSONObject getParamsData(Annotation[] methodParamsAnnotations, Object[] args) {
  36. JSONObject params = null;
  37. if (methodParamsAnnotations == null || methodParamsAnnotations.length == 0) {
  38. return null;
  39. }
  40. params = new JSONObject();
  41. int i = 0;
  42. for (Annotation methodParamsAnnotation : methodParamsAnnotations) {
  43. if (methodParamsAnnotation instanceof Parameter) {
  44. params.put(((Parameter) methodParamsAnnotation).value(), JSONObject.toJSONString(args[i++]));
  45. }
  46. }
  47. return params;
  48. }
  49. /**
  50. * 原方法必须执行
  51. */
  52. private Object sourceMethod(ProceedingJoinPoint joinPoint) {
  53. try {
  54. return joinPoint.proceed();
  55. } catch (Throwable throwable) {
  56. throwable.printStackTrace();
  57. }
  58. return null;
  59. }
  60. /**
  61. * 获取方法的接收参数的注解
  62. */
  63. private Annotation[] getMethodParamsAnnotations(Method method) {
  64. //一个参数可能有多个注解 Annotation[]
  65. Annotation[][] parameterAnnotations = method.getParameterAnnotations();
  66. if (parameterAnnotations.length == 0) {
  67. return null;
  68. }
  69. Annotation[] result = new Annotation[parameterAnnotations.length];
  70. int i = 0;
  71. for (Annotation[] parameterAnnotation : parameterAnnotations) {
  72. for (Annotation annotation : parameterAnnotation) {
  73. result[i++] = annotation;
  74. }
  75. }
  76. return result;
  77. }
  78. }

至此,完成了AspectJ形式的埋点上传,不侵入原代码,埋点信息不影响原逻辑且有统一的入口,方便后续的修改和扩展。

后续的扩展,只需要在需要埋点的方法或者页面、异常等地方加入注解,并仿造DataPoint的方式,新建AspectJ处理类即可。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/266737?site
推荐阅读
相关标签
  

闽ICP备14008679号