首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android: UsageStatsManager没有返回正确的每日结果

Android: UsageStatsManager没有返回正确的每日结果
EN

Stack Overflow用户
提问于 2016-03-26 17:30:37
回答 3查看 8.4K关注 0票数 18

我试图从UsageStats查询UsageStatsManager,目的是返回每天使用的所有应用程序包,以及使用时间多长时间。

代码:

代码语言:javascript
复制
public static List<UsageStats> getUsageStatsList(Context context){
    UsageStatsManager usm = getUsageStatsManager(context);
    Calendar calendar = Calendar.getInstance();
    long endTime = calendar.getTimeInMillis();
    calendar.add(Calendar.DAY_OF_YEAR, -1);
    long startTime = calendar.getTimeInMillis();

    List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,startTime, endTime);
    return usageStatsList;
}

我有一个闹钟,每天午夜前发出警报,查询其usagestats,然后存储返回的数据。起初,一切看起来都很好,我得到了包结果和它们的活动时间,但是我添加了一个函数,可以每小时检查结果,这里是我做了一个奇怪的发现。

来自UsageStatsManager的结果似乎是在不同的时间重置,而不是在午夜,考虑到我使用INTERVAL_DAILY作为搜索参数,这正是我所期望的。

从我保存的包的数据来看,“时间”结果似乎是在重置(,粗略时间,):

  • 凌晨3点
  • 正午
  • 下午3点
  • 午夜

我意识到,当包的时间被重置时,这是有关联的,但这是否意味着会发生呢?

我已经看到了下面的帖子,我从这里获得了很多信息:如何使用UsageStatsManager?

因此:Android UsageStatsManager产生错误的输出?在评论中提到,从queryUsageStats返回的数据不能被信任,并且随机结果正在返回。

我是遗漏了一些简单的东西,还是UsageStatsManager的功能不正常?

EN

回答 3

Stack Overflow用户

发布于 2017-07-28 18:45:52

我也注意到了API 21中的这种行为,UsageStats数据在API 21中的维护时间不够长。它从API 22中运行良好,如果您签入android /data/system/usagestats,您将在API 21中找到有限的条目,因此在API 21中使用它并不可靠。

对于API 21+,您将获得一整天的usagestats,同时根据UsageStatsManager API查询INTERVAL_DAILY。如果您想在一天之内进行查询,您应该使用queryEvents并按照自己的逻辑迭代它们。

我试过以下方法..。

这是用于捕获每个应用程序的数据的模式类:

代码语言:javascript
复制
private class AppUsageInfo {
        Drawable appIcon;
        String appName, packageName;
        long timeInForeground;
        int launchCount;

        AppUsageInfo(String pName) {
            this.packageName=pName;
        }
}

List<AppUsageInfo> smallInfoList; //global var

这是一种方法,它很简单,随流程而去:

代码语言:javascript
复制
void getUsageStatistics() {

UsageEvents.Event currentEvent;
List<UsageEvents.Event> allEvents = new ArrayList<>();
HashMap<String, AppUsageInfo> map = new HashMap <String, AppUsageInfo> ();

long currTime = System.currentTimeMillis();
long startTime currTime - 1000*3600*3; //querying past three hours

UsageStatsManager mUsageStatsManager =  (UsageStatsManager)
                    mContext.getSystemService(Context.USAGE_STATS_SERVICE);

        assert mUsageStatsManager != null;
UsageEvents usageEvents = mUsageStatsManager.queryEvents(usageQueryTodayBeginTime, currTime);

//capturing all events in a array to compare with next element

         while (usageEvents.hasNextEvent()) {
            currentEvent = new UsageEvents.Event();
            usageEvents.getNextEvent(currentEvent);
            if (currentEvent.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND ||
                    currentEvent.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) {
                allEvents.add(currentEvent);
                String key = currentEvent.getPackageName();
// taking it into a collection to access by package name
                if (map.get(key)==null)
                    map.put(key,new AppUsageInfo(key));
            }
        }

//iterating through the arraylist 
         for (int i=0;i<allEvents.size()-1;i++){
            UsageEvents.Event E0=allEvents.get(i);
            UsageEvents.Event E1=allEvents.get(i+1);

//for launchCount of apps in time range
             if (!E0.getPackageName().equals(E1.getPackageName()) && E1.getEventType()==1){
// if true, E1 (launch event of an app) app launched
                 map.get(E1.getPackageName()).launchCount++;
             }

//for UsageTime of apps in time range
            if (E0.getEventType()==1 && E1.getEventType()==2
                    && E0.getClassName().equals(E1.getClassName())){
                long diff = E1.getTimeStamp()-E0.getTimeStamp();
                phoneUsageToday+=diff; //gloabl Long var for total usagetime in the timerange
                map.get(E0.getPackageName()).timeInForeground+= diff;
            }
        }
//transferred final data into modal class object
        smallInfoList = new ArrayList<>(map.values());

}
票数 13
EN

Stack Overflow用户

发布于 2018-06-01 16:52:43

我同意您在评论中提到的关于queryUsageStats不是可信来源的说法。我已经和UsageStatsManager玩了一段时间了,它会根据一天中的时间返回不一致的结果。我发现使用UsageEvent和手动计算所需的信息要可靠得多(至少对于每日统计数据是这样),因为它们是时间点,没有任何奇怪的计算错误,根据一天中的不同时间为相同的输入产生不同的输出。

我使用@Vishal提出的解决方案来提出我自己的解决方案:

代码语言:javascript
复制
/**
 * Returns the stats for the [date] (defaults to today) 
 */
fun getDailyStats(date: LocalDate = LocalDate.now()): List<Stat> {
    // The timezones we'll need 
    val utc = ZoneId.of("UTC")
    val defaultZone = ZoneId.systemDefault()

    // Set the starting and ending times to be midnight in UTC time
    val startDate = date.atStartOfDay(defaultZone).withZoneSameInstant(utc)
    val start = startDate.toInstant().toEpochMilli()
    val end = startDate.plusDays(1).toInstant().toEpochMilli()

    // This will keep a map of all of the events per package name 
    val sortedEvents = mutableMapOf<String, MutableList<UsageEvents.Event>>()

    // Query the list of events that has happened within that time frame
    val systemEvents = usageManager.queryEvents(start, end)
    while (systemEvents.hasNextEvent()) {
        val event = UsageEvents.Event()
        systemEvents.getNextEvent(event)

        // Get the list of events for the package name, create one if it doesn't exist
        val packageEvents = sortedEvents[event.packageName] ?: mutableListOf()
        packageEvents.add(event)
        sortedEvents[event.packageName] = packageEvents
    }

    // This will keep a list of our final stats
    val stats = mutableListOf<Stat>()

    // Go through the events by package name
    sortedEvents.forEach { packageName, events ->
        // Keep track of the current start and end times
        var startTime = 0L
        var endTime = 0L
        // Keep track of the total usage time for this app
        var totalTime = 0L
        // Keep track of the start times for this app 
        val startTimes = mutableListOf<ZonedDateTime>()
        events.forEach {
            if (it.eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
                // App was moved to the foreground: set the start time
                startTime = it.timeStamp
                // Add the start time within this timezone to the list
                startTimes.add(Instant.ofEpochMilli(startTime).atZone(utc)
                        .withZoneSameInstant(defaultZone))
            } else if (it.eventType == UsageEvents.Event.MOVE_TO_BACKGROUND) {
                // App was moved to background: set the end time
                endTime = it.timeStamp
            }

            // If there's an end time with no start time, this might mean that
            //  The app was started on the previous day, so take midnight 
            //  As the start time 
            if (startTime == 0L && endTime != 0L) {
                startTime = start
            }

            // If both start and end are defined, we have a session
            if (startTime != 0L && endTime != 0L) {
                // Add the session time to the total time
                totalTime += endTime - startTime
                // Reset the start/end times to 0
                startTime = 0L
                endTime = 0L
            }
        }

        // If there is a start time without an end time, this might mean that
        //  the app was used past midnight, so take (midnight - 1 second) 
        //  as the end time
        if (startTime != 0L && endTime == 0L) {
            totalTime += end - 1000 - startTime
        }
        stats.add(Stat(packageName, totalTime, startTimes))
    }
    return stats
}

// Helper class to keep track of all of the stats 
class Stat(val packageName: String, val totalTime: Long, val startTimes: List<ZonedDateTime>)

以下是几点意见:

  • Event所具有的时间戳在UTC中,这就是为什么我从默认时区将开始/结束查询时间转换为UTC的原因,以及为什么我将每个事件的开始时间转换回UTC。这个让我有一段时间..。
  • 这考虑到了这样的情况:一个应用程序在一天开始之前就在前台(即用户在午夜之前打开了一个应用程序),或者在结束后进入后台(也就是说,在当天晚上11:59之后,用户在前台仍然有一个应用程序)。免责声明:我还没有真正测试过这些边缘案例。
  • 如果用户在午夜后使用应用程序,我选择使用11:59:59 PM作为结束时间。很明显,您可以将这个值更改为离午夜1毫秒,或者仅仅是午夜,这取决于您选择如何计算。只需移除- 1000并用任何您想要的替换。
  • 在我的用例中,我需要总的前台时间+启动时间,这就是我收集这些信息的原因。但是,您可以调整Stat类和代码,以获取所需的任何信息。例如,您可以跟踪结束时间,或者一天内启动应用程序的次数(如果需要)。
  • 我在这里使用Java 8时代库,因为它更容易处理日期。要在Android中使用这个,我使用ThreeTenABP库。

我希望这能帮到你!

票数 8
EN

Stack Overflow用户

发布于 2017-08-03 11:07:20

我想我找到了那里发生的事。首先我写了下面的代码,

代码语言:javascript
复制
 public String getDaily(String appPackageName, long startTime, long endTime)
 {
    List<UsageStats> usageStatsList = usageStatsManager.queryUsageStats(
                     UsageStatsManager.INTERVAL_DAILY, startTime,endTime);

    String x="";
    for(int i=0; i<usageStatsList.size(); i++) {

        UsageStats stat = usageStatsList.get(i);
        if(stat.getPackageName().equals(appPackageName))
            x=x+i+"-"+stat.getPackageName()+"-"
            +converLongToTimeChar(stat.getTotalTimeInForeground())+"\n";
    }

    return x;
}
public String converLongToTimeChar(long usedTime) {
    String hour="", min="", sec="";

    int h=(int)(usedTime/1000/60/60);
    if (h!=0)
        hour = h+"h ";

    int m=(int)((usedTime/1000/60) % 60);
    if (m!=0)
        min = m+"m ";

    int s=(int)((usedTime/1000) % 60);
    if (s==0 && (h!=0 || m!=0))
        sec="";
    else
        sec = s+"s";

    return hour+min+sec;
}

(今天的日期是03.08.2017 :25:14),当我发送(“包名”,02.08.2017 00.00.00,03.08.2017 00.00.00);方法,(我用日历发送这个日期,你可以搜索谷歌,如何设置这样的日期)我得到了这个输入;

代码语言:javascript
复制
  46-'apppackagename'-9m 31s
  154-'apppackagename'-22m 38s

然后我发送(“包名”,03.08.2017 00.00.00,04.08.2017 00.00.00);

代码语言:javascript
复制
  25-'apppackagename'-22m 38s

我使用了我发送的应用程序,大约1分钟。同样,我发送的方法输出是:

02:08:2017-03.08.2017

代码语言:javascript
复制
  46-'apppackagename'-9m 31s
  154-'apppackagename'-23m 32s

03:08:2017-04.08.2017

代码语言:javascript
复制
  25-'apppackagename'-23m 32s

如你所见,两者都增加了。当我看到我一直等到凌晨3点的时候,我使用了大约5分钟的应用程序,我得到了这些输出。

02:08:2017-03.08.2017

代码语言:javascript
复制
  46-'apppackagename'-9m 31s
  154-'apppackagename'-23m 32s

03:08:2017-04.08.2017

代码语言:javascript
复制
  25-'apppackagename'-23m 32s
  50-'apppackagename'-4m 48s

最后,你应该在一天前和它的最后一个前期运行时间进行控制。İf和那天的第一个前景时间是一样的。你应该去掉这段时间,然后把其他时间的总和还给你。(即使我不知道那个奇怪的系统。)新一天的柜台在凌晨3点后开始。

我希望这对你会有帮助。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36238481

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档