Aspect Android埋点统计activity页面使用时长 onResume onPause,并保存时长
标记:
1.项目下build.gradle
- dependencies {
- classpath 'com.android.tools.build:gradle:3.5.4'
-
- classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
- }
2.app 文件夹下 build.gradle
apply plugin: 'com.hujiang.android-aspectjx'
-
- // 配置 AspectJX
- aspectjx {
- exclude 'androidx.core','androidx.appcompat'
- }
- dependencies {
- .......
- // 引入 AspectJX 依赖
- implementation 'org.aspectj:aspectjrt: 1.9.8'
- ...
- }
-
- import org.aspectj.bridge.IMessage
- import org.aspectj.bridge.MessageHandler
- import org.aspectj.tools.ajc.Main
-
- android.applicationVariants.all { variant ->
- JavaCompile javaCompile = variant.javaCompile
- javaCompile.doLast {
- //下面的1.8是指我们兼容的jdk的版本
- String[] args = [
- "-showWeaveInfo",
- "-1.8",
- "-inpath", javaCompile.destinationDir.toString(),
- "-aspectpath", javaCompile.classpath.asPath,
- "-d", javaCompile.destinationDir.toString(),
- "-classpath", javaCompile.classpath.asPath,
- "-bootclasspath", android.bootClasspath.join(File.pathSeparator)
- ]
-
- MessageHandler handler = new MessageHandler(true);
- new Main().run(args, handler)
- def log = project.logger
- 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:
- case IMessage.INFO:
- log.info message.message, message.thrown
- break;
- case IMessage.DEBUG:
- log.debug message.message, message.thrown
- break;
- }
- }
- }
- }
注意:
aspectjx {。。。} 和
android.applicationVariants.all {。。。。。}
要与 android {...} 同级
示例:
-
- @Aspect
- public class ClickAspect {
- private static final String TAG = ClickAspect.class.getSimpleName();
-
- private Map
resumeTimeMap = new WeakHashMap<>(); - private Map
durationMap = new WeakHashMap<>(); - private Map
eventTrackerMap = new WeakHashMap<>(); -
- @Pointcut("execution(* android.view.View.OnClickListener.onClick(android.view.View))")
- public void onClickPointcut() {
- }
-
-
- @Pointcut("execution(* android.app.Activity.onCreate(..))")
- public void activityOnCreatePointcut() {
-
- }
-
- @Pointcut("execution(* bg.camera.com.MainActivity+.onDestroy(..))")
- public void activityDestroyPointcut() {
-
- }
-
- // 定义切入点,匹配Activity的onResume和onDestroy方法
- @Pointcut("execution(void android.app.Activity.onResume()) && target(activity)")
- public void onActivityResumed(Activity activity) {
- }
-
- @Pointcut("execution(void android.app.Activity.onPause()) && target(activity)")
- public void onActivityPaused(Activity activity) {
- }
- @Pointcut("execution(void android.app.Activity.onDestroy()) && target(activity)")
- public void onActivityDestroy(Activity activity) {
- }
-
- // 在Activity的onCreate方法前进行统计逻辑处理
- @Before("onActivityResumed(activity)")
- public void trackActivityResumed(JoinPoint joinPoint, Activity activity) {
- // 获取Activity的类名
- String className = activity.getClass().getSimpleName();
-
- // 获取当前时间戳
- long startTime = System.currentTimeMillis();
-
-
- // 存储Activity的开始时间戳
- // 可以使用SharedPreferences或其他方式进行持久化存储
- updateTrackPointInfoNameValue(activity, className, startTime);
-
- Log.i(TAG, activity.getClass().getSimpleName() + " onActivityResumed "+startTime);
-
- }
-
- // 在Activity的onDestroy方法后进行统计逻辑处理
- @After("onActivityPaused(activity)")
- public void trackActivityPaused(JoinPoint joinPoint, Activity activity) {
- // 获取Activity的类名
- String className = activity.getClass().getSimpleName();
-
- EventBean starTimeE = getTrackPointInfoNameValue(activity, className);
- long starTime = null != starTimeE ? starTimeE.getEventTime() : 0;
- // 获取停留当前时间戳
- long pausedTime = System.currentTimeMillis() - starTime;
- // 获取存储的开始时间戳
- // 根据开始时间戳和当前时间戳计算使用时长
-
- // 进行统计上报或其他处理逻辑
- updateTrackPointInfoNameValue(activity, className, pausedTime);
- Log.i(TAG, activity.getClass().getSimpleName() + " onActivityPaused "+pausedTime);
-
- }
-
- // 在Activity的onDestroy方法后进行统计逻辑处理
- @After("onActivityDestroy(activity)")
- public void trackActivityDestroy(JoinPoint joinPoint, Activity activity) {
- // 获取Activity的类名
- String className = activity.getClass().getSimpleName();
-
- removeTrackPointInfoNameValue(activity, System.currentTimeMillis());
-
- Log.i(TAG, activity.getClass().getSimpleName() + " onActivityPaused");
-
- // 注销之后Fragment onStop之后的生命周期将不再执行
- // 进行统计上报或其他处理逻辑
- }
-
- @After("onClickPointcut()")
- public void afterOnClick(JoinPoint joinPoint) throws Throwable {
- Object target = joinPoint.getTarget();
- String className = "";
- if (target != null) {
- className = target.getClass().getSimpleName();
- }
- Object[] args = joinPoint.getArgs();
- if (args.length > 0 && args[0] instanceof View) {
- View view = (View) args[0];
- String entryName = view.getResources().getResourceEntryName(view.getId());
- // TrackPoint.onClick(className, entryName);
- }
- // joinPoint.proceed();
- // 点击事件执行后执行的代码
- Log.d(TAG, "Button clicked");
- }
-
-
-
- @Around("activityOnCreatePointcut()")
- public void pageOpen(ProceedingJoinPoint joinPoint) throws Throwable {
- String name = joinPoint.getSignature().toShortString();
- long time = System.currentTimeMillis();
- Object target = joinPoint.getTarget();
- String className = target.getClass().getSimpleName();
- // updateTrackPointInfoNameValue(activity, className, 0L);
- // TrackPoint.onPageOpen(className);
- joinPoint.proceed();
- Log.e(TAG, name + " activityOnCreatePointcut: " + (System.currentTimeMillis() - time));
- }
-
- @Around("activityDestroyPointcut()")
- public void pageClose(ProceedingJoinPoint joinPoint) throws Throwable {
- long time = System.currentTimeMillis();
- Object target = joinPoint.getTarget();
- String className = target.getClass().getSimpleName();
- // TrackPoint.onPageClose(className);
- joinPoint.proceed();
- Log.e(TAG, className + " activityOnCreatePointcut: " + (System.currentTimeMillis() - time));
- }
-
-
- public static boolean hasId(int[] arr, int value) {
- for (int i : arr) {
- if (i == value)
- return true;
- }
- return false;
- }
-
- /**
- * 性能监控
- *
- * AspectJ其实在Android中的应用主要还是性能监控、日志埋点等,下面以一个简单的例子表示:
- * 我们监控布局加载耗时,判断布局是否嵌套过多或者过于复制导致Activity启动卡顿,首先我们知道Activity是通过setContentView方法加载布局的:
- * 1、布局解析过程,IO过程
- * 2、创建View为反射过程
- * 这两步均为耗时操作,所以我们需要监控setContentView
- *
- * 日常开发中,我们可以将耗时上传到服务器,收集用户信息,找到卡顿Activity做出对应优化
-
- * @param point
- * @throws Throwable
- */
- @Around("execution(* android.app.Activity.setContentView(..))")
- public void getContentViewTime(ProceedingJoinPoint point) throws Throwable {
- String name = point.getSignature().toShortString();
- long time = System.currentTimeMillis();
- point.proceed();
- Log.e(TAG, name + " cost: " + (System.currentTimeMillis() - time));
- }
- @Around("call (* bg.eyccamera.com.MyApp.**(..))")
- public void getTime(ProceedingJoinPoint joinPoint) {
- Signature signature = joinPoint.getSignature();
- String name = signature.toShortString();
- long time = System.currentTimeMillis();
- try {
- joinPoint.proceed();
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- }
- Log.i(TAG, name + " cost" + (System.currentTimeMillis() - time));
- }
-
- private void updateTrackPointInfoNameValue(Context context, String name, long time) {
- DatabaseManager.getInstance(context).insertData(new EventBean(name, time));
- }
- private EventBean getTrackPointInfoNameValue(Context context, String name) {
- return getTrackPointInfoNameValue(context, name, 0);
- }
- private EventBean getTrackPointInfoNameValue(Context context, String name, long time) {
- List
eventList = null; - EventBean eventBean = null;
- eventList = DatabaseManager.getInstance(context).getAllData();
- if (null != eventList && eventList.size() > 0) {
- for (int i = 0; i < eventList.size(); i++) {
- if (name.equals(eventList.get(i).getPath())) {
- eventBean = eventList.get(i);
- break;
- }
- }
- }
- return eventBean;
- }
- private void removeTrackPointInfoNameValue(Context context, long evenTime) {
- DatabaseManager.getInstance(context).removeData(evenTime/ 1000);
- }
- }