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&…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
