Android获取应用使用时长和次数-UsageStatsManager使用

最近在做暑研,要求是需要获取到Android手机中的应用使用情况,经过查找,发现Android提供了 UsageStatsManager类来帮助进行应用使用信息的统计(注明,Android5.0系统大部分手机不支持这个类,Android5.1系统之后完全支持[1], 显然现在大部分手机都是支持的),这篇文章主要记录一下这个类的使用方法。

授权

想要使用这个类来获取应用使用信息,首先需要对app进行授权,我们需要在AndroidManifest.xml文件中加入uses-permission

1
2
3
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />

然后需要我们在设置-安全-更多安全设置-使用情况访问权限中给对应的app打开使用权限(华为p30 pro)。我们在代码中也可以进行判断,主动要求用户打开该设置。

1
2
3
4
5
6
7
8
9
// 首先判断获取到的list是否为空 if (packageInfoList == null)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
try {
startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));
} catch (Exception e) {
Toast.makeText(MainActivity.this,"无法开启允许查看使用情况的应用界面",Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}

对象初始化

初始化UsageStatsManager类我们需要以下代码

1
UsageStatsManager usm = (UsageStatsManager)getSystemService(Context.USAGE_STATS_SERVICE);

获取UsageStats

UsageStatsManager提供了一些方法让我们获取应用的UsageStats,比如Map<String, UsageStats> queryAndAggregateUsageStats(long beginTime, long endTime)List<EventsStats> queryEventStats(int intervalType, long beginTime, long endTime),通过传入开始时间,结束时间和一个间隔类型(intervalType)来获取在一定时间范围内的UsageStats列表。其中,Android给了以下几种intervalType

Constants Meaning
int INTERVAL_DAILY An interval type that spans a day.
int INTERVAL_MONTHLY An interval type that spans a month.
int INTERVAL_WEEKLY An interval type that spans a week.
int INTERVAL_YEARLY An interval type that spans a year.
int INTERVAL_BEST An interval type that will use the best fit interval for the given time range.

queryAndAggregateUsageStats默认的间隔类型是INTERVAL_BEST

下面的图片代表了我们传入的时间和我们的间隔类型结合应该得到的时间范围,需要注意的是,我们传入的beginTime和endTime不完全是系统获取app信息的开始和结束时间,beginTime和endTime是包含在开始和结束时间之中,也就是说,如果我们的间隔类型设置为INTERVAL_DAILY, 我们获取的使用信息的开始时间可能是beginTime的当天0时,结束时间可能是endTime的当天23:59,这个时间具体还和时区相关,所以具体情况需要视实际情况而定,更详细的解析可以看这一篇[2] 关于 UsageStatsManager.queryUsageStats 的注意事项及 UsageStatsService 的简单原理_菜鸟的自我修养-CSDN博客

image-20210602210207843

获取应用名,应用包名,使用时长

我们可以通过UsageStats.getPackageName()这个方法来获取应用的包名,通过UsageStats.getTotalTimeInForeground()这个方法获取应用在前台的使用时间。

获取应用的名字我们需要用到PackageManger中的getApplcationLabel(ApplicationInfo info)这个方法,获取ApplicationInfo可以使用getApplicationInfo(String packageName, int flags)这个方法,代码如下

1
2
3
4
5
6
7
8
9
10
11
public String getApplicationNameByPackageName(Context context, String packageName) {
PackageManager pm = context.getPackageManager();
String Name;
try {
Name = pm.getApplicationLabel(pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
} catch (PackageManager.NameNotFoundException e) {
Name = "";
}
return Name;

}

获取应用使用次数

至于应用使用次数,通过查询发现UsageStats中有mLauchCount这个变量,但是Android没有提供对应的get方法,所以我们就需要用反射来获取使用次数 [3]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 利用反射,获取UsageStats中统计的应用使用次数
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int getLaunchCount(UsageStats usageStats) throws IllegalAccessException {
Field field = null;
try {
field = usageStats.getClass().getDeclaredField("mLaunchCount");
} catch (NoSuchFieldException e) {
e.printStackTrace();
Log.e(TAG, "getLaunchCount: NoSuchFieldException");
}
Log.i(TAG, "getLaunchCount: " + "Package Name: " + usageStats.getPackageName() +
"\t" + (int) field.get(usageStats));
return (int) field.get(usageStats);
}

但是实际使用下来的情况发现,得到的数据可能为0,原因不知道为什么,所以还可以通过一种较为复杂的方法进行实现。通过UsageStatsManager.queryEvents(long beginTime, long endTime)获取Android提供的另一个类UsageEvents,遍历可以获取到所有的events,通过getEventType()方法可以得到对应的event type,其中ACTIVITY_PAUSEDACTIVITY_RESUMED分别代表了活动移动到了后台和移动到前台,可以通过这两个活动的次数来计算出应用使用次数。详细代码可以参考[3] android统计手机应用使用时长以及开启次数_github_37271067的博客-CSDN博客

参考资料

[1] Android 5.0以上通过UsageStatsManager类 获取应用使用情况(精品) (qq.com)

[2] 关于 UsageStatsManager.queryUsageStats 的注意事项及 UsageStatsService 的简单原理_菜鸟的自我修养-CSDN博客

[3] android统计手机应用使用时长以及开启次数_github_37271067的博客-CSDN博客

[4] Google Developers