本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17425191.html
前言
UsageStatsManager是用来知晓设备中应用的使用统计。它能给我们提供应用的进入前台动作与时间戳、进入后台的动作与时间戳、上次的使用时间、使用总时长等等信息。此功能在原生的设置-应用-使用统计中有所展示。在一般的场景下,我们可以用此管理开发一些应用使用时长统计、连续使用时长提醒、统计应用被前台化的次数/时间段从而分析应用的使用率。
下面的部分是讲解一些正常应用开发能使用的情况。博客的最后一部分是系统级别的调用,会直接调用framework的代码,让我们能有最大权限的查询与监视应用的使用情况信息。
所需权限
<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)
}
查询当前应用的使用情况桶
其实就是查询当前应用在用户行为中经常使用的习惯,注意只能查自己的应用
@RequiresApi(Build.VERSION_CODES.P)
fun standbyBucket(context: Context) {
val usageStatsManager: UsageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val standbyBucket = usageStatsManager.appStandbyBucket
when (standbyBucket) {
UsageStatsManager.STANDBY_BUCKET_ACTIVE -> {
Log.e("zh", "当前应用在前台运行")
}
UsageStatsManager.STANDBY_BUCKET_WORKING_SET -> {
Log.e("zh", "当前应用是一个活跃的前台应用,会被频繁的切换到前台")
}
UsageStatsManager.STANDBY_BUCKET_FREQUENT -> {
Log.e("zh", "当前应用是一个常用的后台应用")
}
UsageStatsManager.STANDBY_BUCKET_RARE -> {
Log.e("zh", "当前应用是一个不常用的后台应用")
}
//此类型是一个系统级别的被隐藏的类型
// UsageStatsManager.STANDBY_BUCKET_NEVER -> {
// Log.e("zh", "当前应用很少被使用")
// }
}
}
其他api
queryConfigurations(int intervalType, long beginTime, long endTime) 获取指定时间区间内硬件配置信息统计数据。
查看IUsageStatsManager的实现位置(系统级别调用代码在framework里)
接下来我们讲解的部分都是系统级的功能,framework里的调用,直接解除一切限制。所以老生常谈,需要你的应用是系统级的并且最好能framework架包(这个我的个人博客里都有讲解自行搜索),新人请了解普通应用与系统级应用的区别。
上面的例子中使用的都是UsageStatsManager,它只是一个功能封装的管理类,它的功能实现其实大部分是调用了IUsageStatsManager。IUsageStatsManager其实是AIDL的接口类。它在framework的UsageStatsService实现位置是如下图片:
end
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17425191.html