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

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)时,判断新的包名与旧的是否相同,如果不同说明进行了应用切换,记录退出了上个应用和进入了新的应用。


http://www.kler.cn/news/312490.html

相关文章:

  • 2013-2023年专精特新小巨人企业财务指标数据
  • MySQL 数据库备份与恢复指南
  • 抖音下载别人作品怎么去掉水印
  • Spring Boot 集成 Redisson 实现消息队列
  • 【C#生态园】提升C#开发效率:深入了解自然语言处理库与工具
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • 探索未来智能:Moonshot AI 引领AI新纪元——M1超级模型
  • css百分比布局中height:100%不起作用
  • 牛客小白月赛101(栈、差分、调和级数、滑动窗口)
  • Java中out流中打印方法详解
  • 【设计模式-享元】
  • 深度学习后门攻击分析与实现(一)
  • 基于python+django+vue的家居全屋定制系统
  • IntelliJ IDEA 创建 HTML 项目教程
  • 基于SpringBoot+Vue的个性化旅游推荐系统
  • Android MediaPlayer + GLSurfaceView 播放视频
  • leetcode 392.判断子序列
  • MATLAB绘图:5.三维图形
  • 力扣53-最大子序和(Java详细题解)
  • SpringBoot 入门实践
  • Django+React+Neo4j实现的地质领域知识图谱系统
  • CentOS7更新YUM源
  • 9.20哈好
  • 算法【双向广搜】
  • QT Layout布局,隐藏其中的某些部件后,不影响原来的布局
  • 【数据结构】5——哈夫曼树(Huffman Tree)
  • Linux网络——手撕TCP服务器,制定应用层协议,实现网络版计算器
  • websocketpp服务器搭建
  • 使用knn算法对iris数据集进行分类
  • 人力资源数据集分析(一)_t-test、卡方检验和描述性统计