项目中用到了埋点统计功能,最终综合各种方案,选用了AOP切面编程实现统一的无侵入式埋点。
首先简单介绍一下aop,aop为切面编程,主要就是在我们程序编译期,通过java的织入器将我们统一的代码写入到我们指定的地方,避免了我们重复一个地方一个地方的写代码,统一管理并且跟本身没有关联性。
现在主流的aop框架有:AspectJ、ASM、Javasist,大家感兴趣可以了解一下它们的区别。
本文采用的是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的使用语法,使用起来很简单。