Android开发 UsageStatsManager应用使用情况管理

发布时间 2023-05-23 14:54:18作者: 观心静

前言

  UsageStatsManager是用来知晓,设备中应用的使用情况的管理。它能给我们提供应用的进入前台动作与时间戳、进入后台的动作与时间戳、上次的使用时间、使用总时长等等信息。此功能在原生的设置-应用-使用统计中有所展示。

所需权限

<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>

查询当前应用的使用事件

这里使用的是queryEventsForSelf方法,在这个方法返回的数据里包含相同应用每一次进入前后台的时间点。

代码

fun query(context: Context) {
    //查询的开始时间
    val startCalendar = Calendar.getInstance()
    startCalendar.set(Calendar.HOUR_OF_DAY, 0)
    startCalendar.set(Calendar.MINUTE, 0)
    startCalendar.set(Calendar.SECOND, 0)
    //查询的结束时间
    val endCalendar = Calendar.getInstance()
    endCalendar.set(Calendar.HOUR_OF_DAY, 23)
    endCalendar.set(Calendar.MINUTE, 59)
    endCalendar.set(Calendar.SECOND, 59)
    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    var usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

    /**
     * 查询当前应用的使用事件
     */
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val usageEvents = usageStatsManager.queryEventsForSelf(startCalendar.timeInMillis, endCalendar.timeInMillis)
        while (usageEvents.hasNextEvent()) {
            val event = UsageEvents.Event()
            usageEvents.getNextEvent(event)
            when (event.eventType) {
                MOVE_TO_FOREGROUND -> Log.e("zh", "进入前台 ${event.packageName} ${simpleDateFormat.format(event.timeStamp)}")
                MOVE_TO_BACKGROUND -> Log.e("zh", "退出后台 ${event.packageName} ${simpleDateFormat.format(event.timeStamp)}")
                else -> Log.e("zh", "其他事件 ${event.eventType} ${event.packageName} ${simpleDateFormat.format(event.timeStamp)}")
            }
        }
    }
}

结果:

2023-05-23 11:25:37.934 14013-14013 zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 11:24:56
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  退出后台 com.xx.dev 2023-05-23 11:25:33
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 11:25:33
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  退出后台 com.xx.dev 2023-05-23 11:25:34
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 11:25:34
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  退出后台 com.xx.dev 2023-05-23 11:25:35
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 11:25:35
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  退出后台 com.xx.dev 2023-05-23 11:25:37
2023-05-23 11:25:37.935 14013-14013 zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 11:25:37

查询设备全局应用的使用事件

这里使用的是queryEvents方法

代码

fun query(context: Context) {
    //查询的开始时间
    val startCalendar = Calendar.getInstance()
    startCalendar.set(Calendar.HOUR_OF_DAY, 0)
    startCalendar.set(Calendar.MINUTE, 0)
    startCalendar.set(Calendar.SECOND, 0)
    //查询的结束时间
    val endCalendar = Calendar.getInstance()
    endCalendar.set(Calendar.HOUR_OF_DAY, 23)
    endCalendar.set(Calendar.MINUTE, 59)
    endCalendar.set(Calendar.SECOND, 59)
    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    var usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager


    /**
     * 查询设备全局应用的使用事件
     */
    val usageEvents = usageStatsManager.queryEvents(startCalendar.timeInMillis, endCalendar.timeInMillis)
    while (usageEvents.hasNextEvent()) {
        val event = UsageEvents.Event()
        usageEvents.getNextEvent(event)
        when (event.eventType) {
            MOVE_TO_FOREGROUND -> Log.e("zh", "进入前台 ${event.packageName} ${simpleDateFormat.format(event.timeStamp)}")
            MOVE_TO_BACKGROUND -> Log.e("zh", "退出后台 ${event.packageName} ${simpleDateFormat.format(event.timeStamp)}")
        }
    }
}

结果

2023-05-23 14:09:23.051  6144-6144  zh                      com.xx.dev                         E  进入前台 com.android.launcher3 2023-05-23 14:08:57
2023-05-23 14:09:23.051  6144-6144  zh                      com.xx.dev                         E  退出后台 com.android.launcher3 2023-05-23 14:09:05
2023-05-23 14:09:23.051  6144-6144  zh                      com.xx.dev                         E  进入前台 com.xx.dev 2023-05-23 14:09:05
2023-05-23 14:09:23.051  6144-6144  zh                      com.xx.dev                         E  退出后台 com.xx.dev 2023-05-23 14:09:08

查询设备的应用使用统计情况

请注意下面的输入了时间间隔类型UsageStatsManager.INTERVAL_BEST,但是不代表返回的数值就是准确的,比如如果我查询中午12点至下午六点的使用统计,实际上它依然会返回今天应用的全部使用情况

代码

fun query(context: Context) {
    //查询的开始时间
    val startCalendar = Calendar.getInstance()
    startCalendar.set(Calendar.HOUR_OF_DAY, 0)
    startCalendar.set(Calendar.MINUTE, 0)
    startCalendar.set(Calendar.SECOND, 0)
    //查询的结束时间
    val endCalendar = Calendar.getInstance()
    endCalendar.set(Calendar.HOUR_OF_DAY, 23)
    endCalendar.set(Calendar.MINUTE, 59)
    endCalendar.set(Calendar.SECOND, 59)
    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    var usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

    /**
     * 查询设备的应用使用统计情况
     */
    val list = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startCalendar.timeInMillis, endCalendar.timeInMillis)
    for (item in list) {
        Log.e("zh", "包名:${item.packageName}")
        Log.e("zh", "开始时间:${simpleDateFormat.format(item.firstTimeStamp)}")
        Log.e("zh", "上次时间戳:${simpleDateFormat.format(item.lastTimeStamp)}")
        Log.e("zh", "最后使用时间:${simpleDateFormat.format(item.lastTimeUsed)}")
        Log.e("zh", "前台总的时间:${item.totalTimeInForeground}")
    }
}

结果

2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  包名:com.android.server.telecom
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  开始时间:2023-05-23 09:44:08
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  上次时间戳:2023-05-23 14:23:13
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  最后使用时间:1970-01-01 08:00:00
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  前台总的时间:0
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  包名:android.ext.services
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  开始时间:2023-05-23 09:44:08
2023-05-23 14:28:04.162  9020-9020  zh     com.xx.dev     E  上次时间戳:2023-05-23 14:23:13
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  最后使用时间:1970-01-01 08:00:00
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  前台总的时间:0
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  包名:com.mediatek.capctrl.service
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  开始时间:2023-05-23 09:44:08
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  上次时间戳:2023-05-23 14:23:13
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  最后使用时间:1970-01-01 08:00:00
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  前台总的时间:0
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  包名:net.huanci.hsj
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  开始时间:2023-05-23 09:44:08
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  上次时间戳:2023-05-23 14:23:13
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  最后使用时间:2023-05-23 11:10:10
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  前台总的时间:11449
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  包名:com.android.smspush
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  开始时间:2023-05-23 09:44:08
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  上次时间戳:2023-05-23 14:23:13
2023-05-23 14:28:04.163  9020-9020  zh     com.xx.dev     E  最后使用时间:1970-01-01 08:00:00

查询给定范围内的所有统计数据(使用该范围的最佳间隔)

这里使用的是queryAndAggregateUsageStats方法,这个方法里不需要传入时间间隔参数,并且返回的是map集合。

代码

fun query(context: Context) {
    //查询的开始时间
    val startCalendar = Calendar.getInstance()
    startCalendar.set(Calendar.HOUR_OF_DAY, 0)
    startCalendar.set(Calendar.MINUTE, 0)
    startCalendar.set(Calendar.SECOND, 0)
    //查询的结束时间
    val endCalendar = Calendar.getInstance()
    endCalendar.set(Calendar.HOUR_OF_DAY, 23)
    endCalendar.set(Calendar.MINUTE, 59)
    endCalendar.set(Calendar.SECOND, 59)
    val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

    var usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

    val map = usageStatsManager.queryAndAggregateUsageStats(startCalendar.timeInMillis, System.currentTimeMillis())
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        map.forEach { key, value ->
            Log.e("zh", "包名:${key}")
            Log.e("zh", "开始时间:${simpleDateFormat.format(value.firstTimeStamp)}")
            Log.e("zh", "上次使用时间戳:${simpleDateFormat.format(value.lastTimeStamp)}")
            Log.e("zh", "最后使用时间:${simpleDateFormat.format(value.lastTimeUsed)}")
            Log.e("zh", "前台总的时间:${value.totalTimeInForeground}")
        }
    }
}

结果

2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  包名:net.huanci.hsj
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  开始时间:2023-05-23 09:44:08
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  上次使用时间戳:2023-05-23 14:23:13
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  最后使用时间:2023-05-23 11:10:10
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  前台总的时间:11449
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  包名:com.android.smspush
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  开始时间:2023-05-22 09:38:07
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  上次使用时间戳:2023-05-23 14:23:13
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  最后使用时间:1970-01-01 08:00:23
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  前台总的时间:0
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  包名:com.android.settings
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  开始时间:2023-05-22 09:38:07
2023-05-23 14:38:09.392 11063-11063 zh   com.xx.dev   E  上次使用时间戳:2023-05-23 09:38:07
2023-05-23 14:38:09.393 11063-11063 zh   com.xx.dev   E  最后使用时间:2023-05-23 09:33:18
2023-05-23 14:38:09.393 11063-11063 zh   com.xx.dev   E  前台总的时间:3328776

时间间隔类型

在上面的方法中,有要求输入查询时间的间隔类型,下面一共提供了4种间隔类型。

/**
 * 跨越一天的间隔类型。参见{@link queryUsageStats(int, long, long)}。
 */
public static final int INTERVAL_DAILY = 0;

/**
 * 跨越一周的间隔类型。参见{@link queryUsageStats(int, long, long)}。
 */
public static final int INTERVAL_WEEKLY = 1;

/**
 * 跨越一个月的间隔类型。参见{@link queryUsageStats(int, long, long)}。
 */
public static final int INTERVAL_MONTHLY = 2;

/**
 * 跨越一年的间隔类型。参见{@link queryUsageStats(int, long, long)}。
 */
public static final int INTERVAL_YEARLY = 3;

/**
 * 一种间隔类型,它将使用给定时间范围的最佳拟合间隔。参见{@link queryUsageStats(int, long, long)}。
 */
public static final int INTERVAL_BEST = 4;

查询当前应用是否活跃

代码

@RequiresApi(Build.VERSION_CODES.M)
fun isAppInactive(context: Context, packageName:String):Boolean {
    val usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
    return usageStatsManager.isAppInactive(packageName)
}

其他api

queryConfigurations(int intervalType, long beginTime, long endTime) 获取指定时间区间内硬件配置信息统计数据。

end