【问题标题】:How to use UsageStatsManager?如何使用 UsageStatsManager?
【发布时间】:2014-12-13 10:29:22
【问题描述】:

背景

Google 已弃用“ActivityManager”类的函数“getRecentTasks”。现在它所做的就是获取当前应用已打开的应用列表。

我什至写了一篇关于它的帖子here on StackOverflow,但我发现这是不可能的。

问题

我已经发了一篇关于它的帖子(here,以及另一个由其他人创建的类似的帖子,here)并要求重新考虑它,Google 决定开设一个新课程,这似乎提供类似的功能(更像是统计信息,但也可能有用),但我不知道如何使用它。

这个类被称为“UsageStatsManager”,我猜是函数“queryUsageStats”完成了这项工作。

此外,它似乎有一个新权限(“android.permission.PACKAGE_USAGE_STATS”),这是一个系统权限,但它是这样写的:

声明权限意味着使用 API 和用户的意图 的设备可以通过设置应用程序授予权限。

Here's 另一个关于此新功能的链接。

我发现了什么

我查看了 Android 的代码,注意到“上下文”有 USAGE_STATS_SERVICE ,在 JavaDocs 中是这样说的:

/**
 * Use with {@link #getSystemService} to retrieve a {@link
 * android.app.UsageStatsManager} for interacting with the status bar.
 *
 * @see #getSystemService
 * @see android.app.UsageStatsManager
 * @hide
 */
public static final String USAGE_STATS_SERVICE = "usagestats";

奇怪的是,它不仅说“status bar”,而且 packageName 也不匹配(应该是“android.app.usage.UsageStatsManager”)。

我还添加了正确的权限:

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

这是我使用的代码:

  final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
  final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
  final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

在模拟器本身中,我去了“Settings"-&gt;"security"-&gt;"apps with usage access”,并启用了我的应用程序。

但是,在运行代码时,我得到的只是一个空列表...

问题

你如何使用UsageStatsManager

另外,您如何让用户以最简单的方式授予权限?还是在应用尝试获取所需信息时自动完成?

尝试使用此类但用户尚未确认时会发生什么?

如何让代码返回一个真实的应用列表?

【问题讨论】:

  • "如何让用户以最简单的方式授予权限?" -- 我在“设置”>“安全”屏幕中看到“具有使用访问权限的应用程序”。据推测,请求此权限的应用程序会出现在那里。恕我直言,他们应该使用&lt;meta-data&gt;
  • @CommonsWare 是的,我注意到了这一点,但我作为开发人员提出了这个问题。开发人员应该如何处理它?我什至不知道如何初始化整个事情。这也有点奇怪,它是系统权限,但可以使用它......在不同的情况下有这样的事情吗?也许这将是一个类似的处理......
  • "开发者应该如何处理它?" - 告诉用户去访问那个屏幕,我想。 “另外一个案子有这样的事吗?” -- 我不知道。
  • @CommonsWare 我的意思是,它是如何工作的?我什至如何初始化或到达这个类?它没有 CTOR,也不能通过 Context.getSystemService 获得(至少不在文档和 SDK 中)......另外,当我尝试调用这个函数时会发生什么?它会进入所需的设置屏幕,还是我应该自己做?我需要希望如何使用这个新课程...
  • 我的猜测是它应该是通过getSystemService() 并且存在文档错误。至于其他的,我不知道。它的源代码似乎还没有发布。

标签: android usage-statistics activity-manager android-recents usagestatsmanager


【解决方案1】:

其实有一个example app included in AOSP sample code:developers/samples/android/system/AppUsageStatistics/

它包括在应用程序中使用UsageStats 所需的所有位:

  1. Declaring the permission in AndroidManifest.xml

    &lt;uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/&gt;

  2. Show settings to grant permission to access UsageStatsManager

    startActivity(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS));

  3. Query UsageStatsManager for statistics.

    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.YEAR, -1);
    List<UsageStats> queryUsageStats = mUsageStatsManager
            .queryUsageStats(intervalType, cal.getTimeInMillis(),
                    System.currentTimeMillis());
    
  4. Creating a list of apps based on UsageStats

总结并应用于您的示例:

  • 您似乎正确地请求并授予访问使用情况统计信息的权限。
  • 您正确获取了UsageStats 系统服务。
  • 但是,您查询的时间段太短:参数beginTimeendTime 以自纪元以​​来的毫秒数为单位。 Calendar 实例可以通过 getTimeinMillis() 为您提供此值。你错误地做的是只给出年份数字(20152017,如果你今天要运行该程序)。这些值被解释为自纪元以来的毫秒数,因此间隔只有 2 毫秒长,是 1970 年的某个时间。

您应该复制我在下面发布的示例,而不是您发布的以下代码 sn-p:

错误:

final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

正确:

final long currentTime = System.currentTimeMillis(); // Get current time in milliseconds

final Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -2); // Set year to beginning of desired period.
final long beginTime = cal.getTimeInMillis(); // Get begin time in milliseconds

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginTime, currentTime);

【讨论】:

  • 如果您已经将时间设置为比当前时间早一年,那么“intervalType”到底是什么?
  • @android-developer: intervalType 是聚合的粒度统计信息。 Official documentation 有更多细节。还有一种方便的方法queryAndAggregateUsageStats(long, long) 会尝试自动找到最佳区间类型。
  • 我不明白。这里有什么要聚合的?它不只是显示使用统计信息吗?我查看了 UsageStats 文档(此处:developer.android.com/reference/android/app/usage/…),这对我来说似乎很简单......例如,如果我选择一个月来汇总,这意味着什么? “getLastTimeUsed”是否会仅返回上个月范围内的时间,以及前一个月的第二个时间?
  • @android-developer,最好查看UsageStatsManager 中的queryUsageStats(),您需要查询统计信息。在那里,您会看到在每个间隔中为每个包返回UsageStats。假设您查询一年并想要每月间隔。这意味着该列表可能包含 12 个 UsageStats 元素,用于同一个包(如果该应用在间隔内的 12 个月中的每一个月都使用。
  • 原来是我写的那样?
【解决方案2】:

如果您想查看特定时间段的使用统计信息,您必须首先计算从纪元开始和结束时间段的时间长度(以毫秒为单位)。 (epoch 是自 1970 年 1 月 1 日星期四 00:00:00 UTC 以来经过的秒数。)或者,正如我将在下面的示例代码中展示的那样,一种简单的方法是以毫秒为单位从当前时间向后计算。

例如,如果你想要过去 4 天的使用情况统计,你可以使用以下代码:

UsageStatsManager mUsageStatsManager = (UsageStatsManager) this.getSystemService(Context.USAGE_STATS_SERVICE);    
List<UsageStats> queryUsageStats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
                    (System.currentTimeMillis() - 345600000), System.currentTimeMillis());

数字 345600000 是 4 天的毫秒数。

对于INTERVAL_TYPE this article 解释得很好。

系统在 4 个不同的时间间隔内收集和汇总数据 它们是:INTERVAL_DAILY、INTERVAL_WEEKLY、INTERVAL_MONTHLY 和 INTERVAL_YEARLY。系统记录时间有限,所以你会 能够每天间隔检索长达 7 天的应用使用数据, 每周间隔最长 4 周,每月最长 6 个月,以及 最终每年最多 2 年。

还有第五种选择: INTERVAL_BEST 将选择四个之间的最佳拟合区间 以上基于您选择的时间跨度。

【讨论】:

  • 但是你使用了 INTERVAL_TYPE
  • 所以我们最多可以使用 6 个月?我不明白间隔类型的意义是什么,如果我们已经在准确的时间给它一个间隔......
  • INTERVAL_TYPE 可帮助您查看选择区间的统计信息。对于我使用的上述 sn-p,统计信息将显示过去 4 天内每天应用程序包的使用情况。如果您只想查看您指定的时间间隔(开始和结束时间)的使用统计信息,请查看 queryAndAggregateUsageStats(long, long)。否则,INTERVAL_TYPE 将以 4 种间隔类型(每日、每周、每月或每年)之一生成使用情况统计信息。
  • 再一次,我不明白为什么他们必须添加额外的参数。为什么他们不能只有一个时间参数,它告诉从哪个时间段获取数据,就是这样。如果有一些限制,请告诉他们......
  • 可能不相关,但您能否说明为什么您在 this 上调用 #getSystemService 而不是直接调用它?
【解决方案3】:

回答您的最后一个问题“如何让代码返回给我一个真实的应用列表?”。 queryUsageStats 以毫秒为单位获取开始时间和结束时间,而不是 int 中的年份值。

Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);

Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());

这应该返回 2012 年和 2013 年的 UsageStats 列表(请记住,结束时间不包括最终结果时间范围)。

【讨论】:

  • 这很奇怪,正如文档所说: beginTime = 2013 endTime = 2015 。似乎现在可以工作了。为什么他们需要间隔?无论如何我给他们范围......另外,你是否知道如何开始向用户询问此权限的意图?似乎是用户手动启用的权限。
  • 是的,文档一点都不清楚。目前还不清楚为什么他们需要一个间隔以及开始和结束时间,但据我推断,UsageStatsManger 将数据存储在每日、每周、每月和每年的存储桶中,这些存储桶有自己的开始和结束时间。例如,每日存储桶从早上 5:30 开始。因此,如果您提供每日间隔以及 2014 年 10 月 30 日 00:00 的开始时间和 2014 年 10 月 30 日 23:59 的结束时间,则 queryUsageStats 将返回 10/29 和 10/30 的数据。我可能错了,但这是我理解的在模拟器上测试。
  • 我不明白你的理解。 :(
【解决方案4】:

你可以这样做

//noinspection ResourceType
final UsageStatsManager usageStatsManager=(UsageStatsManager)context.getSystemService("usagestats");// Context.USAGE_STATS_SERVICE);
final int currentYear=Calendar.getInstance().get(Calendar.YEAR);
final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY,currentYear-2,currentYear);

【讨论】:

  • UsageStatsManager.INTERVAL_YEARLY 是什么意思?
  • 提供对使用历史和统计数据的访问。使用数据汇总为时间间隔:超过一年developer.android.com/reference/android/app/usage/…
  • 我不明白。如果要最近2年的数据,为什么要间隔一年?如果您选择不同的间隔,结果会有多大不同?
  • 如果您选择 INTERVAL_DAILY 将显示今天的统计数据 如果您选择 INTERVAL_MONTHLY 将显示月份统计数据等等希望有所帮助
  • 但是如果我想显示最近 2 年的统计数据呢?
【解决方案5】:

当用户打开通知抽屉或锁定屏幕时,使用 UsageStats 会得到错误信息。您必须使用 UsageStatsManager.queryEvents() 并使用 MOVE_TO_FOREGROUND 事件类型查找最新事件。

【讨论】:

  • 为什么会出错?你能否展示一下它应该如何的代码,以展示它如何更好?
  • 我刚刚在三星 Galaxy S5 6.0.1 和摩托罗拉 Moto G5 Plus 8.1.0 上测试了这个问题。当屏幕被锁定时,最新的UsageStats 有一个android 的包,而不是最后使用的应用程序。你说的是这个问题吗?
【解决方案6】:

我在我的 Github 上创建了一个如何使用 UsageStats 的示例。 希望它可以对某人有所帮助

https://github.com/ColeMurray/UsageStatsSample

【讨论】:

  • 谢谢。你得到+1的努力。 :)
  • 代码在假设您何时获得所需权限时有点错误,因为它假设如果结果为空,则您没有权限。我尝试过使用这些解决方案:stackoverflow.com/q/7203668/878126,但无论发生什么,它们都返回了相同的结果(拒绝)。我在这里写了一篇关于它的帖子:stackoverflow.com/q/28921136/878126
  • 会不会是隐藏权限?^
【解决方案7】:

我认为文档只是日历内容的简写。我认为它实际上不适用于 2014 年。但是我可能是错的。

为了访问实际的 UsageStats 列表,您需要创建一个具有正确月、日和年的 Calendar 对象。 MRK 在另一个答案中是怎么说的。我复制并更正了 MRK 代码中的错误,以便将来看到它的任何人都可以看到。

Calendar beginCal = Calendar.getInstance();
beginCal.set(Calendar.DATE, 1);
beginCal.set(Calendar.MONTH, 0);
beginCal.set(Calendar.YEAR, 2012);

Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.DATE, 1);
endCal.set(Calendar.MONTH, 0);
endCal.set(Calendar.YEAR, 2014);

final List<UsageStats> queryUsageStats=usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_YEARLY, beginCal.getTimeInMillis(), endCal.getTimeInMillis());

-信用 MRK;由我纠正(他不小心把 cal 而不是 beginCal 和 endCal)

使用权限设置的代码如下。 :)

Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);

【讨论】:

  • 我怎么会错过呢?也许他们在我查看后添加了它。无论如何,有没有办法准确地访问应用程序以启用/禁用?这样用户只需要确认?
  • 我认为不可能。如果您查看 ACTION_USAGE_ACCESS_SETTINGS 下的输入,您会注意到它什么也没说。因此,我认为没有办法指定某个应用程序。
  • 太糟糕了。现在我不知道如何处理我的问题的答案,因为你和 MRK 都回答了它的不同部分。对我来说唯一缺少的是对如何使用它的解释,以及确切的“间隔”是什么。我还想知道它是否可以在普通设备上运行,而不仅仅是模拟器,因为它看起来好像是系统权限。
  • 它可以在设备上运行。您可以只检查程序包的活动时间来确定是否正在使用活动,或者我认为您可以使用 queryEvents() 来查看应用程序是否已移动到前台或后台。老实说,它真的会阻止用户使用你的应用程序。如果用户是第一次使用您的应用,那么去给予特别许可会让他们感到害怕/奇怪。
  • 我修复了MRK的代码,并把它合二为一,方便以后参考。他忘记使用 beginCal 和 endCal 而不是 cal。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多