You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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>)

错误原因分析

  1. 周起始时间计算错误:calendar.set(Calendar.WEEK_OF_YEAR, -1) 写法无效,未正确定位到当前周起始日期,也未将时间重置到当天0点,导致时间范围包含非目标周期数据。
  2. 每日查询时间范围错误:循环中每次调用queryUsageStats时,结束时间用了当前系统时间,导致每次查询都会包含从周起始到现在的所有数据,而非仅当天数据。
  3. 应用使用列表未重置:usedAppsList在循环中未清空,前几天的数据被累加到后续日期中,造成每日数据计算错误。
  4. 生成未来日期:固定循环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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:12:03