Rya*_*Dev 8 java android usage-statistics activity-manager usagestatsmanager
我试图查询UsageStats的UsageStatsManager,与返回的日常使用所有应用程序包的出发点和多长时间.
代码:
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;
}
Run Code Online (Sandbox Code Playgroud)
我有一个警报,每天在午夜之前触发并查询用户状态,然后存储返回的数据.起初一切似乎工作正常,我得到包结果和他们的活动时间,但是我添加了一个功能,每小时检查一次结果,这是我发现一个奇怪的发现.
结果UsageStatsManager似乎是在不同的时间重置,而不是在午夜,这是我所期望的,考虑到我INTERVAL_DAILY用作搜索参数.
根据我保存的数据包"时间"结果似乎重置(粗略时间):
我意识到封装时序重置之间存在相关性,但这是否意味着发生?
我已经看过以下线程了,我从中得到了很多信息: 如何使用UsageStatsManager?
因此:
Android UsageStatsManager产生错误的输出?
在评论中提到,返回的数据queryUsageStats不可信,并且返回随机结果.
我错过了一些简单的东西还是UsageStatsManager运作不正常?
小智 8
我也注意到了API 21中的这种行为,在API 21中,UsageStats数据的维护时间不长.它可以在API 22中正常工作.如果您签入android /data/system/usagestats,您将在API 21中找到有限的条目,因此在API中使用它是不可靠的21.
对于API 21+,您将根据API usagestats查询一整天.如果您想在一天中的几小时内查询,您应该使用并按照您自己的逻辑迭代它们.INTERVAL_DAILYUsageStatsManagerqueryEvents
我试着用以下方式......
这是捕获每个应用程序数据的模态类:
private class AppUsageInfo {
Drawable appIcon;
String appName, packageName;
long timeInForeground;
int launchCount;
AppUsageInfo(String pName) {
this.packageName=pName;
}
}
Run Code Online (Sandbox Code Playgroud)
List<AppUsageInfo> smallInfoList; //global var
这里是方法,它很容易,顺其自然:
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());
}
Run Code Online (Sandbox Code Playgroud)
I agree with what is said in that comment you mentioned about queryUsageStats not being a trusted source. I've been with playing with the UsageStatsManager for a little while and it returns inconsistent results based on the time of day. I have found that using the UsageEvents and manually calculating the necessary info to be much more trustworthy (at least for daily stats), as they are points in time and don't have any weird calculating errors that would produce different outputs for the same input depending on the time of day.
I used @Vishal's proposed solution to come up with my own:
/**
* 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>)
Run Code Online (Sandbox Code Playgroud)
A couple of observations:
Events have are in UTC, which is why I convert my start/end query times to UTC from my default time zone, and why I convert the start time back on each event. This one got me for a while...- 1000 and replace with whatever you want. Stat class and the code to capture whatever info you need. You can keep track of end times, or number of times an app was launched in a day if needed for example. I hope this helps!
| 归档时间: |
|
| 查看次数: |
3923 次 |
| 最近记录: |