使用UsageStatsManager获取周应用使用数据返回结果异常
UsageStatsManager 获取周应用使用数据逻辑问题排查与修复
问题现象
- 当天为周二时,周一、周二的使用数据计算错误
- 返回了本周尚未到来的日期数据
用户原实现代码
获取周使用数据函数
suspend fun getWeeklyUsage(): WeeklyUsage = withContext(Dispatchers.IO) { val pm: PackageManager = context.packageManager var weeklyUsage = WeeklyUsage( weekScreenTime = 0, weekDuration = "", dailyUsages = emptyList(), ) try { val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager val dailyUsages: MutableList<DailyUsage> = mutableListOf() var usedAppsList: MutableList<AppUsage> = mutableListOf() val calendar = Calendar.getInstance() calendar.set(Calendar.WEEK_OF_YEAR, -1) calendar.set(Calendar.DAY_OF_WEEK, calendar.firstDayOfWeek) var startTime = calendar.timeInMillis val endTime = System.currentTimeMillis() val dateFormat = SimpleDateFormat("d MMMM yyyy", Locale.getDefault()) Log.e("Usage Stats", "Start time: ${formatMillisTime(startTime)}, End time: ${formatMillisTime(endTime)}") for (day in 0 until 7) { val usageStatsList = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, startTime, endTime, ) for (usageStats in usageStatsList) { val packageName = usageStats.packageName val totalScreenTime = usageStats.totalTimeInForeground if (totalScreenTime > 0) { val screenTimeDuration = getDuration(totalScreenTime) val app = try { val packageInfo = pm.getPackageInfo(packageName, 0) val appName = packageInfo.applicationInfo.loadLabel(pm).toString() val appIcon = packageInfo.applicationInfo.loadIcon(pm) App( packageName = packageName, name = appName, icon = appIcon, ) } catch (e: Exception) { Log.e("Screen Usage Repository", "Error: ${e.message}") App( packageName = packageName, name = "", ) } val appUsage = AppUsage( app = app, appDuration = screenTimeDuration, appScreenTime = totalScreenTime, ) usedAppsList.add(appUsage) } } val date = dateFormat.format(Date(startTime)) val dayScreenTime = usedAppsList.sumOf(AppUsage::appScreenTime) val dayDuration = getDuration(dayScreenTime) usedAppsList = usedAppsList.sortedByDescending(AppUsage::appScreenTime).toMutableList() val dailyUsage = DailyUsage( date = date, dayScreenTime = dayScreenTime, dayDuration = dayDuration, mostUsedApps = usedAppsList, ) dailyUsages.add(dailyUsage) calendar.add(Calendar.DAY_OF_WEEK, 1) startTime = calendar.timeInMillis } val weekScreenTime = dailyUsages.sumOf(DailyUsage::dayScreenTime) val screenTimeDuration = getDuration(weekScreenTime) weeklyUsage = WeeklyUsage( weekScreenTime = weekScreenTime, weekDuration = screenTimeDuration, dailyUsages = dailyUsages, ) Log.e("Screen Usage", "Total Screen Usage ${weeklyUsage.weekDuration}") weeklyUsage.dailyUsages.forEach { dailyUsage -> Log.e("Screen Usage", "Date: ${dailyUsage.date}, Screen Time: ${dailyUsage.dayDuration}") dailyUsage.mostUsedApps.forEach { Log.e("Screen Usage", "App: ${it.app.name}, Screen Time: ${it.appDuration}") } } weeklyUsage } catch (e: Exception) { Log.e("Screen Usage Repository", "Error: ${e.message}") weeklyUsage } }
数据类定义
data class App(val packageName: String?, val name: String, val icon: Drawable? = null) data class AppUsage(val app: App, val appDuration: String, val appScreenTime: Long) data class DailyUsage(val date: String, val dayScreenTime: Long, val dayDuration: String, val mostUsedApps: MutableList<AppUsage>) data class WeeklyUsage(val weekScreenTime: Long, val weekDuration: String, val dailyUsages: List<DailyUsage>)
错误原因分析
- 周起始时间计算错误:
calendar.set(Calendar.WEEK_OF_YEAR, -1)写法无效,未正确定位到当前周起始日期,也未将时间重置到当天0点,导致时间范围包含非目标周期数据。 - 每日查询时间范围错误:循环中每次调用
queryUsageStats时,结束时间用了当前系统时间,导致每次查询都会包含从周起始到现在的所有数据,而非仅当天数据。 - 应用使用列表未重置:
usedAppsList在循环中未清空,前几天的数据被累加到后续日期中,造成每日数据计算错误。 - 生成未来日期:固定循环7次,不管当前是周几,都会生成本周未到来的日期数据,不符合需求。
修复后的代码
suspend fun getWeeklyUsage(): WeeklyUsage = withContext(Dispatchers.IO) { val pm: PackageManager = context.packageManager val weeklyUsage = WeeklyUsage( weekScreenTime = 0, weekDuration = "", dailyUsages = emptyList(), ) try { val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager val dailyUsages: MutableList<DailyUsage> = mutableListOf() val dateFormat = SimpleDateFormat("d MMMM yyyy", Locale.getDefault()) val currentCalendar = Calendar.getInstance() // 设置为当天0点,用于判断是否超过当前日期 currentCalendar.set(Calendar.HOUR_OF_DAY, 0) currentCalendar.set(Calendar.MINUTE, 0) currentCalendar.set(Calendar.SECOND, 0) currentCalendar.set(Calendar.MILLISECOND, 0) val currentDayStart = currentCalendar.timeInMillis // 获取当前周的起始日期(按系统firstDayOfWeek定义) val weekStartCalendar = Calendar.getInstance() weekStartCalendar.set(Calendar.DAY_OF_WEEK, weekStartCalendar.firstDayOfWeek) // 重置到当天0点,避免包含上一周的剩余时间 weekStartCalendar.set(Calendar.HOUR_OF_DAY, 0) weekStartCalendar.set(Calendar.MINUTE, 0) weekStartCalendar.set(Calendar.SECOND, 0) weekStartCalendar.set(Calendar.MILLISECOND, 0) var currentDayCalendar = weekStartCalendar.clone() as Calendar while (currentDayCalendar.timeInMillis <= currentDayStart) { val dayStartTime = currentDayCalendar.timeInMillis // 设置当天结束时间:第二天0点,当天则用当前系统时间 val dayEndTime = currentDayCalendar.apply { add(Calendar.DAY_OF_YEAR, 1) }.timeInMillis val actualEndTime = if (dayStartTime == currentDayStart) System.currentTimeMillis() else dayEndTime val usedAppsList: MutableList<AppUsage> = mutableListOf() val usageStatsList = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, dayStartTime, actualEndTime, ) for (usageStats in usageStatsList) { val packageName = usageStats.packageName val totalScreenTime = usageStats.totalTimeInForeground if (totalScreenTime > 0) { val screenTimeDuration = getDuration(totalScreenTime) val app = try { val packageInfo = pm.getPackageInfo(packageName, 0) val appName = packageInfo.applicationInfo.loadLabel(pm).toString() val appIcon = packageInfo.applicationInfo.loadIcon(pm) App( packageName = packageName, name = appName, icon = appIcon, ) } catch (e: Exception) { Log.e("Screen Usage Repository", "Error: ${e.message}") App( packageName = packageName, name = "", ) } val appUsage = AppUsage( app = app, appDuration = screenTimeDuration, appScreenTime = totalScreenTime, ) usedAppsList.add(appUsage) } } val date = dateFormat.format(Date(dayStartTime)) val dayScreenTime = usedAppsList.sumOf(AppUsage::appScreenTime) val dayDuration = getDuration(dayScreenTime) val sortedApps = usedAppsList.sortedByDescending(AppUsage::appScreenTime).toMutableList() val dailyUsage = DailyUsage( date = date, dayScreenTime = dayScreenTime, dayDuration = dayDuration, mostUsedApps = sortedApps, ) dailyUsages.add(dailyUsage) // 进入下一天 currentDayCalendar.add(Calendar.DAY_OF_YEAR, 1) } val weekScreenTime = dailyUsages.sumOf(DailyUsage::dayScreenTime) val screenTimeDuration = getDuration(weekScreenTime) WeeklyUsage( weekScreenTime = weekScreenTime, weekDuration = screenTimeDuration, dailyUsages = dailyUsages, ).also { result -> Log.e("Screen Usage", "Total Screen Usage ${result.weekDuration}") result.dailyUsages.forEach { dailyUsage -> Log.e("Screen Usage", "Date: ${dailyUsage.date}, Screen Time: ${dailyUsage.dayDuration}") dailyUsage.mostUsedApps.forEach { Log.e("Screen Usage", "App: ${it.app.name}, Screen Time: ${it.appDuration}") } } } } catch (e: Exception) { Log.e("Screen Usage Repository", "Error: ${e.message}") weeklyUsage } }
修复说明
- 周起始时间修正:正确获取当前周的起始日期,并重置到当天0点,确保时间范围准确。
- 每日时间范围修正:为每个日期设置独立的开始和结束时间,当天的结束时间用当前系统时间,避免包含未来数据。
- 重置应用列表:每次循环都重新初始化
usedAppsList,确保每日数据独立计算。 - 避免未来日期:使用
while循环,只生成从周起始到当前日期的记录,不会包含本周未到来的日期。
内容的提问来源于stack exchange,提问作者Abdul Lateef Sherani
相关产品推荐
相关产品推荐

