当前位置: 首页 > news >正文

android10 系统定制:增加应用使用数据埋点,应用使用时长统计

需求意在统计应用的使用时长和开始结束时间,最终生成一个文件可以直观看出什么时候进入了哪个应用、什么时候退出,如图:
在这里插入图片描述
每行记录了应用的进入或退出,以逗号分割。分别记录了事件开始时间,应用包名,进入或退出(1或2),应用名称。
根据上面的数据记录可以看出:2024-08-12 09:52:54进入了设置,09:52:57退出设置回到了桌面,09:53:11进入了包名为com.example.intelligentsearch888名称为client1的应用… …
基本思路:当系统窗体焦点发生变化时,获取应用信息,如果进入了新的应用(同一个应用内不做记录,减少不必要的数据)则写入记录文件。
具体实现:
1.添加数据记录帮助类:
在frameworks/base/services/core/java/com/下添加自定义目录custom/buriedpoint,添加工具文件BuriedPointManager.java:

 package com.custom.buriedpoint;import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.text.TextUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;/*** @author : ljl  * @description:* @date :2024/8/9 下午5:35*/
public class BuriedPointManager {/*** Enter application buried point type*/private static final int TYPE_BURIED_POINT_DATA_ENTER = 1;/*** Exit application buried point type*/private static final int TYPE_BURIED_POINT_DATA_EXIT = 2;/*** Embedded point file path*/private static final String BURIED_POINT_FILE_PATH = "/data/system/UsageStats.txt";/*** Time conversion format** @param createTime* @return*/private String getSimpleDateTime(long createTime) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");try {long issueTime = new Date(createTime).getTime();String timeStamp = dateFormat.format(new Date(issueTime));return timeStamp;} catch (NumberFormatException e) {e.printStackTrace();}return "";}/*** Write buried point file** @param appName* @param packageName* @param type*/private void saveBuriedPointFile(String appName, String packageName, int type) {File file = new File(BURIED_POINT_FILE_PATH);if (!file.exists()) {try {file.createNewFile();} catch (IOException e) {e.printStackTrace();}}StringBuilder sb = new StringBuilder();sb.append(getSimpleDateTime(System.currentTimeMillis()));sb.append(",");sb.append(packageName);sb.append(",");sb.append(type);sb.append(",");sb.append(appName);android.util.Log.i("fzs-buried", "save content == " + sb.toString());FileWriter writer = null;try {writer = new FileWriter(BURIED_POINT_FILE_PATH, true);writer.write(sb.toString());writer.write("\n");} catch (IOException e) {e.printStackTrace();} finally {try {if (writer != null) {writer.close();}} catch (IOException e) {e.printStackTrace();}}}public void onNewFocusChange(String newPackageName, String newAppName, boolean isFocus) {saveBuriedPointFile(newPackageName, newAppName,isFocus ? TYPE_BURIED_POINT_DATA_ENTER : TYPE_BURIED_POINT_DATA_EXIT);}
}

代码比较简单,主要定义了日志文件的输出路径,进入或退出应用的常量和写入到文件的方法。
2.埋点数据的写入
系统窗口焦点的变化可以在WindowManagerService中获取到,所以主要在WMS中进行数据写入:

 final class H extends android.os.Handler {private BuriedPointManager buriedPointManager;private String lastPackageName=""; //记录上个应用包名private String lastAppName=""; //记录上个应用名称...public void handleMessage(Message msg) {switch (msg.what) {case REPORT_FOCUS_CHANGE: {if (lastFocus == newFocus) {// Focus is not changing, so nothing to do.return;}if (buriedPointManager == null) {//实例化工具类buriedPointManager = new BuriedPointManager();}PackageManager packageManager = mContext.getPackageManager();if (lastFocus != null){try {ApplicationInfo applicationInfoLastFocus = packageManager.getApplicationInfo(lastFocus.getOwningPackage(),PackageManager.GET_META_DATA);if (applicationInfoLastFocus!=null){//获取lastFocus 对应的包名和应用名称,记录到全局变量lastPackageName = packageManager.getApplicationLabel(applicationInfoLastFocus).toString();lastAppName = lastFocus.getOwningPackage();}} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}}if (newFocus != null){try {ApplicationInfo applicationInfoNewFocus = packageManager.getApplicationInfo(newFocus.getOwningPackage(),PackageManager.GET_META_DATA);if (applicationInfoNewFocus!=null){String newPackageName = packageManager.getApplicationLabel(applicationInfoNewFocus).toString();if (!TextUtils.equals(newPackageName, lastPackageName)) {//发生了应用切换if (!TextUtils.isEmpty(lastPackageName)) {//记录退出了上个应用buriedPointManager.onNewFocusChange(lastPackageName,lastAppName,false);}if (!TextUtils.isEmpty(newPackageName)) {// 记录进入了新的应用buriedPointManager.onNewFocusChange(newPackageName,newFocus.getOwningPackage(),newFocus.isFocused());}}}} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}}synchronized (mGlobalLock) {displayContent.mLastFocus = newFocus;if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +" to " + newFocus + " displayId=" + displayContent.getDisplayId());if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Delaying loss of focus...");displayContent.mLosingFocus.add(lastFocus);lastFocus = null;}}...break;}...}...}

系统窗口焦点发生变化会调用Handler的REPORT_FOCUS_CHANGE,里面有lastFocus和newFocus记录了新旧Window的状态。
因为执行到这里lastFocus可能为null只存在newFocus,因此在Handler定义了全局变量lastPackageName和lastAppName来记录上次的应用包名及名称。根据lastFocus获取到上个窗体的应用包名及名称。当新的窗体到来(newFocus!=null)时,判断新的包名与旧的是否相同,如果不同说明进行了应用切换,记录退出了上个应用和进入了新的应用。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【uni-app】小兔鲜项目-基础架构-请求和上传文件拦截器
  • 大数据最新面试题(持续更新)
  • 语音识别与语音控制的原理介绍
  • C++的初阶模板和STL
  • 漫步者头戴式耳机怎么样?漫步者、西圣、索尼三大耳机测评对比
  • 1.3 MySql的用户管理
  • 基于STM32红外感应的自动迎客人语音控制系统设计
  • .NET Core中的时区转换问题
  • Java设计模式—面向对象设计原则(五) ----->迪米特法则(DP) (完整详解,附有代码+案例)
  • 生信初学者教程(五):R语言基础
  • PCL 读取txt格式点云并可视化
  • Cron表达式学习
  • 机器狗与无人机空地协调技术分析
  • 自注意力与多头自注意力的区别
  • 基于yolov5的不同颜色安全帽检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
  • SegmentFault for Android 3.0 发布
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • interface和setter,getter
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JavaScript DOM 10 - 滚动
  • jQuery(一)
  • Lucene解析 - 基本概念
  • nginx 负载服务器优化
  • OSS Web直传 (文件图片)
  • PHP 7 修改了什么呢 -- 2
  • unity如何实现一个固定宽度的orthagraphic相机
  • 对象管理器(defineProperty)学习笔记
  • 回流、重绘及其优化
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 区块链将重新定义世界
  • 小程序button引导用户授权
  • 携程小程序初体验
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (vue)页面文件上传获取:action地址
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (补)B+树一些思想
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)...
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (算法二)滑动窗口
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET 设计一套高性能的弱事件机制
  • .NET技术成长路线架构图
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc
  • .NET值类型变量“活”在哪?
  • /bin/rm: 参数列表过长"的解决办法
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [1127]图形打印 sdutOJ