Android AOP统计埋点,无侵入实现

项目中用到了埋点统计功能,最终综合各种方案,选用了AOP切面编程实现统一的无侵入式埋点。

一、什么是AOP

首先简单介绍一下aop,aop为切面编程,主要就是在我们程序编译期,通过java的织入器将我们统一的代码写入到我们指定的地方,避免了我们重复一个地方一个地方的写代码,统一管理并且跟本身没有关联性。

现在主流的aop框架有:AspectJ、ASM、Javasist,大家感兴趣可以了解一下它们的区别。

本文采用的是AspectJ进行统一埋点处理。

二、AspectJ配置。

1.首先在项目build.gradle中加入:

 classpath 'org.aspectj:aspectjtools:1.8.9'
 classpath 'org.aspectj:aspectjweaver:1.8.9'
buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
 
 
allprojects {
    repositories {
        google()
        jcenter()
    }
}
 
task clean(type: Delete) {
    delete rootProject.buildDir
}

2.在app下的build.gradle中加入一下配置

 implementation 'org.aspectj:aspectjrt:1.8.9'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
final def log = project.logger
final def variants = project.android.applicationVariants
 
variants.all { variant ->
    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return;
    }
 
    JavaCompile javaCompile = variant.javaCompile
    javaCompile.doLast {
        String[] args = ["-showWeaveInfo",
                         "-1.8",
                         "-inpath", javaCompile.destinationDir.toString(),
                         "-aspectpath", javaCompile.classpath.asPath,
                         "-d", javaCompile.destinationDir.toString(),
                         "-classpath", javaCompile.classpath.asPath,
                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
        log.debug "ajc args: " + Arrays.toString(args)
 
        MessageHandler handler = new MessageHandler(true);
        new Main().run(args, handler);
        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break;
                case IMessage.WARNING:
                    log.warn message.message, message.thrown
                    break;
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break;
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break;
            }
        }
    }
}

以上就配置好了,接下来开始我们的重头戏,统一埋点实现。

三、统一埋点实现

package com.pxwx.common.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 描述: 用于埋点统计的注解类
 * Created By liweidong on 2020/7/30
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Trace {

    //是否在方法前加,默认为true
    boolean isBefore() default true;

    //自定义事件
    String traceId();

    //备用字段   注意使用可变数组传递值形式为:  key=value
    String[] reserve() default "";
}

上面注解类,我们使用的话,在想要埋点的方法上加上此注解就可以了。

package com.pxwx.common.aop;

import android.text.TextUtils;
import android.util.Log;

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;

/**
 * 描述: 埋点织入代码处理类
 * 注意使用可变数组传递值形式为:  key=value
 * Created By liweidong on 2020/7/30
 */

@Aspect
public class TraceAspectJ {

    private static final String POINTCUT_METHOD =
            "execution(@com.pxwx.common.aop.Trace * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void annoHaviorTrace() {

    }

    @Around("annoHaviorTrace()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Trace trace = methodSignature.getMethod().getAnnotation(Trace.class);
        String[] reserve = trace.reserve();
        String traceId = trace.traceId();
        boolean isBefore = trace.isBefore();
        Object result;
        if (isBefore){
            if (!TextUtils.isEmpty(traceId)){
                //多组埋点
                if (reserve != null && reserve.length != 0){
                    TraceUtils.track(traceId, reserve);
                }else{
                    //只有自定义事件
                    TraceUtils.track(traceId);
                }
            }
            result = joinPoint.proceed();
        }else{
            result = joinPoint.proceed();
            if (!TextUtils.isEmpty(traceId)){
                //多组埋点
                if (reserve != null && reserve.length != 0){
                    TraceUtils.track(traceId, reserve);
                }else{
                    //只有自定义事件
                    TraceUtils.track(traceId);
                }
            }
        }
        return result;
    }

}

以上是aspectj织入代码逻辑,大家可以查看一下AspectJ的使用语法,使用起来很简单。

你可能感兴趣的:(android,android,java)