赞
踩
一个项目实现之后,我们并不知道用户对某个部分的使用频率是对少,为了更好的来对项目各个功能的使用统计,我们需要做一些数据埋点的功能,也就是每当用户点击按钮的时候,都对这次点击进行保存处理,然后再之后统一上传到服务器,进行数据分析。
假如,当前有两个方法进行数据埋点:登录和注册。
功能表的数据结构如下:功能id、操作次数、操作人id。
public class FunctionsTable { //功能Id private long functionId; //操作次数 private int operateCounts; //操作用户Id private long operatorId; public long getFunctionId() { return functionId; } public void setFunctionId(long functionId) { this.functionId = functionId; } public int getOperateCounts() { return operateCounts; } public void setOperateCounts(int operateCounts) { this.operateCounts = operateCounts; } public long getOperatorId() { return operatorId; } public void setOperatorId(long operatorId) { this.operatorId = operatorId; } }
下载我们需要做的就是,每次用户点登陆和注册的时候,把数据库中的operateCount字段每次+1。
首先,最直接的实现方法也就是封装一个保存数据的数据库静态操作方法,然后将每次需要操作的地方传入functionId,来进行操作。
这种方法看似可行,但是实际操作的时候会发现很可能需要修改原来的代码结构,同时,一旦function的数量到达一定量以后,这时候产品告诉我们,需求要改,还需要加入额外的操作,这时候一旦运气不好,比如增加一个方法参数,所有调用方法地方都得修改一遍。这时候就比较麻烦了。
有没有什么更好的方法?肯定是有的。首先分析上述方案,它是一个单一重复的调用过程,唯一的区别就是传入的functionId,这是一个单一却又重复的操作,那很明显,aop思想来解决这种问题是最好的。
首先我们选用aop一个常用的类库:aspectj。因此这里我们通过字节码插桩的方式,修改编译之后的class来进行代码的自动生成,这样就不会对我们敲代码的逻辑产生任何影响。
非常简单,就是两个点击事件,模拟一下登陆注册的点击。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnLogin = findViewById(R.id.btn_login); Button btnRegister = findViewById(R.id.btn_register); btnLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("Statistics", "登陆"); } }); btnRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("Statistics", "注册"); } }); } }
1.新建一个枚举类来定义需要埋点的功能:登陆、注册
public enum Function { LOGIN(1, "登陆"), REGISTER(2, "注册"); int functionId; String functionName; Function(int functionId, String functionName) { this.functionId = functionId; this.functionName = functionName; } public String getFunctionName() { return functionName; } }
2.新建一个注解类来标记需要统计点击次数的方法:
使用的时候只需要将需要统计的方法加上注解即可
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Statistics {
Function function();
}
3.Aspectj的简单实现:
@Aspect public class StatisticsInstrumentation { public static final String TAG = "Statistics"; @Around("execution(@com.noob.databurialpoint.Statistics * *(..)) && @annotation(statistics)") public void aroundJoinPoint(ProceedingJoinPoint joinPoint, Statistics statistics) throws Throwable { calculate(statistics); joinPoint.proceed();//执行原方法 } private void calculate(Statistics statistics){ if(statistics != null){ Log.e(TAG, "对" + statistics.function().getFunctionName() + "进行统计"); // select * from FunctionsTable where operatorId=statistics.getFunctionId() //if(size > 0){ // int counts = operateCounts ++ // update FunctionsTable set operateCounts = counts // }else { // insert into FunctionsTable values (xxx, statistics.getFunctionId(), 1) // } } } }
代码解释:
@Around("execution(@com.noob.databurialpoint.Statistics * *(..)) && @annotation(statistics)")
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnLogin = findViewById(R.id.btn_login); Button btnRegister = findViewById(R.id.btn_register); btnLogin.setOnClickListener(new View.OnClickListener() { @Override @Statistics(function = Function.LOGIN) public void onClick(View v) { Log.e("Statistics", "登陆"); } }); btnRegister.setOnClickListener(new View.OnClickListener() { @Override @Statistics(function = Function.REGISTER) public void onClick(View v) { Log.e("Statistics", "注册"); } }); } }
测试结果:
相信大家也都看到了,我们根本没有对之前的方法进行修改,唯一的区别就是在调用方法上添加一了一个
@Statistics注解,如果我们需要修改逻辑,也只需要修改一次aspectJ的实现类StatisticsInstrumentation即可,这样就开发的时候就非常方便。
究其原因是因为aspectj修改了MainActivity.class类,修改后编译生成的class代码如下:
不再只是一个简单的log打印,而是回去调用我们额外写的StatisticsInstrumentation中的方法.这就是aspectj的作用。
aspectj是一个很好的aop框架,此处只是aspectj的一个简单使用示例,关于更深入的用法这里就不再介绍,大家可以去网上寻找相关代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。