实操AspectJ实现Android埋点以及问题汇总
Posted 隔壁小王66
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实操AspectJ实现Android埋点以及问题汇总相关的知识,希望对你有一定的参考价值。
公司为了安全考虑,决定自己做埋点统计,之前做了一版,查询了很多资料,大多数都是在baseActivity监听声明周期以及拦截触摸事件,第一版任务急,也就采用这种方式,配合手动埋点,算是完成第一版的埋点组件。
但是这种有很多问题,比如弹窗,popwindow,以及fragment无法监听,所以,私下查询资料,学习了AspectJ,动手完成了第二版埋点组件,自测兼容所有的点击事件。
AspectJ埋点方案
1:在root build.gradle中添加
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
2:新建module,在build.gradle中添加
apply plugin: 'android-aspectjx'
3:在app的build.gradle中添加
apply plugin: 'android-aspectjx'
构建一下就具备了调用AspectJ的基础
例如
/**
* author : wangchang
* date : 2019-11-29 10:55
* desc : aop实现监听方法
*/
@Aspect
public class AspectJController
/**
* 第一个*所在的位置表示的是返回值,*表示的是任意的返回值,.. 所在位置是方法参数的位置,.. 表示的是任意类型、任意个数的参数
* Pointcut 切入点,告诉代码注入工具,在何处注入一段特定代码的表达式。
*/
@Pointcut("execution(* onClick(..))")
public void onClick()
@Around("onClick()")
public void onClickMethodAround(final ProceedingJoinPoint joinPoint) throws Throwable
Object target = joinPoint.getTarget();
String className = "";
if (target != null)
className = target.getClass().getName();
if (className.contains("$"))
className = className.split("\\\\$")[0];
if (className.contains("_ViewBinding"))
className = className.split("_ViewBinding")[0];
//获取点击事件view对象及名称,可以对不同按钮的点击事件进行统计
Object[] args = joinPoint.getArgs();
if (args.length >= 1 && args[0] instanceof View)
View view = (View) args[0];
int id = view.getId();
if (id < 0)
AspectJManager.onClick(className,"");
else
String entryName = view.getResources().getResourceEntryName(id);
AspectJManager.onClick(className, entryName);
joinPoint.proceed();//执行原来的代码
@Around("execution(* onResume()) && within(android.support.v4.app.Fragment)")
public void onFragmentResume(final ProceedingJoinPoint joinPoint) throws Throwable
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
AspectJManager.onFragmentOpen(className);
joinPoint.proceed();
@Around("execution(* onPause()) && within(android.support.v4.app.Fragment)")
public void onFragmentPause(final ProceedingJoinPoint joinPoint) throws Throwable
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
AspectJManager.onFragmentClose(className);
joinPoint.proceed();
@Around("execution(* onHiddenChanged(..))&& within(android.support.v4.app.Fragment)")
public void onHiddenChanged(final ProceedingJoinPoint joinPoint) throws Throwable
//访问目标方法的参数
Object[] args = joinPoint.getArgs();
boolean hidden = false;
if (args != null && args.length > 0 && args[0].getClass() == Boolean.class)
hidden = (boolean) args[0];
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
if (hidden)
AspectJManager.onFragmentClose(className);
else
AspectJManager.onFragmentOpen(className);
joinPoint.proceed();
@Around("execution(* setUserVisibleHint(..))&& within(android.support.v4.app.Fragment)")
public void setUserVisibleHint(final ProceedingJoinPoint joinPoint) throws Throwable
Object[] args = joinPoint.getArgs();
boolean hidden = false;
if (args != null && args.length > 0 && args[0].getClass() == Boolean.class)
hidden = (boolean) args[0];
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
if (hidden)
AspectJManager.onFragmentOpen(className);
else
AspectJManager.onFragmentClose(className);
joinPoint.proceed();
@Pointcut("execution(* onResume()) && within(com.yunshuxie.bearword.base.BaseActivityM)")
public void openActivity()
@Pointcut("execution(* onDestroy()) && within(com.yunshuxie.bearword.base.BaseActivityM)")
public void closeActivity()
@Around("openActivity()")
public void openActivityMethodAround(final ProceedingJoinPoint joinPoint) throws Throwable
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
AspectJManager.onActivityOpen(className);
joinPoint.proceed();
@Around("closeActivity()")
public void closeActivityMethodAround(final ProceedingJoinPoint joinPoint) throws Throwable
Object target = joinPoint.getTarget();
String className = target.getClass().getName();
AspectJManager.onActivityClose(className);
joinPoint.proceed();
@Aspect: 注解表明这是一个AspectJ文件,编译器在编译的时候,就会自动去解析,然后将代码注入到相应的JPonit中。
这是一个主要的方法,不需要手动调用,会自动调用
通过监听
@Pointcut("execution(* onClick(..))")
监听所有的点击事件,并在onClickMethodAround方法中执行相应操作
通过如下监听页面切换
@Pointcut("execution(* onResume()) && within(xxx.BaseActivityM)")
public void openActivity()
@Pointcut("execution(* onDestroy()) && within(xxx.BaseActivityM)")
public void closeActivity()
通过onFragmentResume,onFragmentPause,onHiddenChanged,setUserVisibleHint来监听fragment的显示隐藏
注意的问题
1:onItemCick问题,项目中集成了BaseRecyclerViewAdapterHelper,运用了它自带的onItemCick点击事件,需要将item
layout设置根id,然后可获取到点击id
2:类名被混淆问题,在release版本测试中,发现BaseRecyclerViewAdapterHelper实现的列表点击,类名被混淆了,需要添加混淆。
3:在继承DialogFragment的弹窗中,发现类名被混淆了,需要添加相应混淆
4:获取的类名会有包含特殊符号的情况,需要过滤,例如在集成butterknife_compiler时
5:需要判断获取到的id小于0的情况,小于0的时候回去view的id名称会崩溃,需要兼容一下
具体可见github
语法参考
https://blog.csdn.net/innost/article/details/49387395
以上是关于实操AspectJ实现Android埋点以及问题汇总的主要内容,如果未能解决你的问题,请参考以下文章