Aspect Android埋点统计activity页面使用时长 onResume onPause,并保存时长
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.Mainandroid.applicationVariants.all { variant ->JavaCompile javaCompile = variant.javaCompilejavaCompile.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.loggerfor (IMessage message : handler.getMessages(null, true)) {switch (message.getKind()) {case IMessage.ABORT:case IMessage.ERROR:case IMessage.FAIL:log.error message.message, message.thrownbreak;case IMessage.WARNING:case IMessage.INFO:log.info message.message, message.thrownbreak;case IMessage.DEBUG:log.debug message.message, message.thrownbreak;}}}
}
注意:
aspectjx {。。。} 和
android.applicationVariants.all {。。。。。}
要与 android {...} 同级
示例:
@Aspect
public class ClickAspect {private static final String TAG = ClickAspect.class.getSimpleName();private Map<String, Long> resumeTimeMap = new WeakHashMap<>();private Map<String, Long> durationMap = new WeakHashMap<>();private Map<String, Boolean> 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<EventBean> 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);}
}
相关文章:
Aspect Android埋点统计activity页面使用时长 onResume onPause,并保存时长
Aspect Android埋点统计activity页面使用时长 onResume onPause,并保存时长 标记: 1.项目下build.gradle dependencies {classpath com.android.tools.build:gradle:3.5.4classpath com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10 } 2.…...
第四章 选择结构程序设计
C语言有两种选择语句:(1)if语句,实现两个分支的选择结构; (2)Switch语句,实现多分支的选择结构。 1.求ax^2bxc0方程的解。 #include<stdio.h> #include<math.h> int …...
JAVA高级教程-Java List(2)
目录 3、List接口的使用(1)3、List接口的使用(3)4、排序,集合之间的转换 3、List接口的使用(1) package ArrayList01;import java.util.ArrayList; import java.util.Iterator; import java.ut…...
Spark--经典SQL50题
目录 连接数据库准备工作 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数 2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数 3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩 4、查询平均成绩…...
数据结构: AVL树
目录 1.AVL树的概念 2.AVL树的模拟实现 AVL树的结构定义 插入 对平衡因子的讨论 旋转 对旋转情况的讨论 1.单旋 1.1左单旋 1.2右单旋 2.双旋 2.1左右双旋 2.2右左双旋 检查是否是AVL树 1.AVL树的概念 当向二叉搜索树中插入新结点后,如果能保证每个结点…...
LeetCode-高频 SQL 50 题:连接 篇
目录 1378. 使用唯一标识码替换员工ID 题目描述: SQL语句: 1068. 产品销售分析 I 题目描述: SQL语句: 1581. 进店却未进行过交易的顾客 题目描述: SQL语句: 197. 上升的温度 题目描述࿱…...
操作系统备考学习 day10
操作系统备考学习 day10 第三章 内存管理3.2 虚拟内存管理3.2.1 虚拟内存的基本概念传统存储管理方式的特征、缺点局部性原理虚拟内存的定义和特征如何实现虚拟内存技术 3.2.2 请求分页管理方式页表机制缺页中断机构地址变换机构 3.2.3 页面置换算法最佳置换算法(OP…...
基于侏儒猫鼬优化的BP神经网络(分类应用) - 附代码
基于侏儒猫鼬优化的BP神经网络(分类应用) - 附代码 文章目录 基于侏儒猫鼬优化的BP神经网络(分类应用) - 附代码1.鸢尾花iris数据介绍2.数据集整理3.侏儒猫鼬优化BP神经网络3.1 BP神经网络参数设置3.2 侏儒猫鼬算法应用 4.测试结果…...
ios safari 正则兼容问题
背景: 系统是自己开发的采购管理系统; 最近升级系统之后客户反馈部分苹果手机现在在进入单据界面的时候报错, 内容显示不全; 安卓手机正常; 苹果首页是之前有使用过系统的才不行, 如果是之前没有使用过系统, 现在也是可以(后面查证这一点可能不是很准确, 跟是否等过过系统…...
Win10下基于VS2015编译SQLite3源码
一、下载SQLite SQLite SQLite Download Page 下载红框部分的3个文件 提示:这里有个 sglite-autoconf-3420000.tar.gz 是免编译版,想省事就下载这个,但我自己用这个老是编译不过 所以我这里不推荐这个了 二、配置SQLite 打开vs 2015或者其他…...
Linux 指令学习
Linux 指令学习 以此为记录,也方便自己日后查看回顾! Linux命令基础格式 无论是什么命令,用于什么用途,在Linux中,命令有其通用的格式: command: 命令本身 options:[可选…...
前端渲染后端返回的HTML格式的数据
在日常开发中,经常有需要前端渲染后端返回页面的需求,对于不同数据结构,前端的渲染方式也不尽相同,本文旨在对各种情况进行总结。 后端返回纯html文件格式 数据包含html标签等元素,数据类型如下图: 前端通…...
身份证读卡器ubuntu虚拟机实现RK3399 Arm Linux开发板交叉编译libdonsee.so找不到libusb解决办法
昨天一个客户要在RK3399 Linux开发板上面使用身份证读卡器,由于没有客户的开发板,故只能用本机ubuntu虚拟机来交叉编译,用客户发过来的交叉编译工具,已经编译好libusb然后编译libdonsee.so的时候提示找不到libusb,报错…...
触想五代强固型工业一体机在近海船舶上的应用
1、行业发展背景 近海船舶的发展紧密关联着海上运输、渔业贸易、旅游开发、能源探测等多领域,带动区域经济、文化繁荣发展。 随着现代科学与信息技术在各行各业的作用增强,工业4.0带动的产业升级逐步渗透进船舶领域,在此背景下,船…...
Node-创建Web应用
题记 node创建web应用,以下是所有流程和代码 与php比较:使用 PHP 来编写后端的代码,需要 Apache 或者 Nginx 的 HTTP 服务器,并配上 mod_php5 模块和 php-cgi。 Node应用的组成 node应用由三部分组成: require 指令&a…...
Redis查找并删除key
redis安装在IP为x.x.x.x的服务器上 redis安装 第一步,安装编译工具及库文件。 命令:yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel 第二步,下载redis安装包。 命令:cd /usr/local/src wget ht…...
Spring Security认证架构介绍
在之前的Spring Security:总体架构中,我们讲到Spring Security整个架构是通过Bean容器和Servlet容器对过滤器的支持来实现的。我们将从过滤器出发介绍Spring Security的Servlet类型的认证架构。 1.AbstractAuthenticationProcessingFilter AbstractAut…...
提升代码重用性:模板设计模式在实际项目中的应用
在软件开发中,我们经常面临着相似的问题,需要使用相同的解决方法。当我们希望将这种通用的解决方法抽象出来,并在不同的情境中重复使用时,就可以使用设计模式中的模板模式(Template Pattern)。模板模式是一…...
11-k8s-service网络
文章目录 一、网络相关资源介绍二、开启ipvs三、nginx网络示例四、pod之间的访问示例五、service反向代理示例 一、网络相关资源介绍 Servcie介绍 Service是对一组提供相同功能的Pods的抽象,并为它们提供一个统一的入口。借助Service,应用可以方便的实现…...
MyBatisPlus(二十二)代码生成器
使用场景 使用代码生成器,根据数据库表,自动生成对应的 Entity,Mapper,Service,Controller 。 代码 依赖 两个依赖: 生成器依赖模板依赖 <dependency><groupId>com.baomidou</groupId&…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
