如何在Kotlin中精准获取Android设备其他应用的使用时长?
精准统计Android应用使用时长的可行方案
针对你遇到的UsageStatsManager返回数据误差大、部分应用失效的问题,结合你提到的AppBlocker在旧设备上的精准表现,以下是无需依赖高API级别AppUsageStatistics的解决思路:
一、优化UsageStatsManager的查询逻辑
不要局限于最简调用,通过调整查询参数和数据处理逻辑提升精度:
- 细化时间切片:避免一次性查询大时间范围的统计数据,改用
INTERVAL_HOURLY或INTERVAL_DAILY拆分查询后合并结果,减少系统统计的聚合误差。 - 区分统计类型:遍历
UsageStats时,重点提取getTotalTimeForeground()(前台活跃时长),而非总运行时长,过滤后台进程的无效统计。 - 匹配进程与包名:部分应用存在多进程情况,需结合
ActivityManager.getRunningAppProcesses()获取进程对应的包名,补全UsageStats中缺失的统计项。 - 强制权限校验:确保已获取
PACKAGE_USAGE_STATS权限,代码中需引导用户跳转到系统设置开启:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); startActivity(intent);
二、基于AccessibilityService的自主追踪
这是旧设备上实现精准统计的核心方案(推测AppBlocker即采用此方式):
- 注册
AccessibilityService,监听TYPE_WINDOW_STATE_CHANGED事件,实时捕获当前前台窗口的包名。 - 维护本地时间戳记录:当窗口切换到目标应用时记录进入时间,切换离开时计算时长并存储到本地数据库,完全自主统计,不受系统API误差影响。
- 关键代码示例(服务内事件处理):
@Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { String packageName = event.getPackageName().toString(); long currentTime = System.currentTimeMillis(); // 从本地读取上一个活跃应用的进入时间 if (lastPackage != null && !lastPackage.equals(packageName)) { long duration = currentTime - lastEnterTime; // 将lastPackage的时长存入本地存储 saveAppUsage(lastPackage, duration); } lastPackage = packageName; lastEnterTime = currentTime; } }
- 注意:需在Manifest中声明服务并配置权限,引导用户在系统设置中开启辅助功能授权。
三、特殊应用的兼容处理
针对部分应用(如后台持续运行的服务、多进程应用)的统计失效问题:
- 结合
ActivityManager.getRunningTasks()(API 21后需特殊权限,低版本可用)辅助验证前台应用状态。 - 对于系统应用,可通过读取
UsageStats中的getLastTimeUsed()结合时间差,补全缺失的时长数据。
内容的提问来源于stack exchange,提问作者c0ffex_
相关产品推荐
相关产品推荐

