我编写了一个更干净、更安全的queryEvents函数,受到@Vishal Sharma, @jguerinet和@Floarian答案的启发。
从Android Q开始,前台服务事件也可以记录。特别是如果您想计算在后台花费的时间,现在可以做到。
创建一个AppUsageStats类:
public class AppUsageStats {
private final long lastTimeUsedMillis;
private final long totalTimeInForegroundMillis;
private final long lastTimeForegroundServiceUsedMillis;
private final long totalTimeForegroundServiceUsedMillis;
public AppUsageStats(
long lastTimeUsedMillis,
long totalTimeInForegroundMillis,
long lastTimeForegroundServiceUsedMillis,
long totalTimeForegroundServiceUsedMillis
) {
this.lastTimeUsedMillis = lastTimeUsedMillis;
this.totalTimeInForegroundMillis = totalTimeInForegroundMillis;
this.lastTimeForegroundServiceUsedMillis = lastTimeForegroundServiceUsedMillis;
this.totalTimeForegroundServiceUsedMillis = totalTimeForegroundServiceUsedMillis;
}
public long getLastTimeUsedMillis() {
return lastTimeUsedMillis;
}
public long getTotalTimeInForegroundMillis() {
return totalTimeInForegroundMillis;
}
@RequiresApi(Build.VERSION_CODES.Q)
public long getLastTimeForegroundServiceUsedMillis() {
return lastTimeForegroundServiceUsedMillis;
}
@RequiresApi(Build.VERSION_CODES.Q)
public long getTotalTimeForegroundServiceUsedMillis() {
return totalTimeForegroundServiceUsedMillis;
}
}
创建一个AppUsageStatsBucket类来存储前台服务数据:
public class AppUsageStatsBucket {
private long startMillis;
private long endMillis;
private long totalTime;
public AppUsageStatsBucket() {
this.startMillis = 0L;
this.endMillis = 0L;
this.totalTime = 0L;
}
public long getStartMillis() {
return startMillis;
}
public void setStartMillis(long startMillis) {
this.startMillis = startMillis;
}
public long getEndMillis() {
return endMillis;
}
public void setEndMillis(long endMillis) {
this.endMillis = endMillis;
}
public long getTotalTime() {
return totalTime;
}
public void addTotalTime() {
this.totalTime += endMillis - startMillis;
}
public void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
}
创建一个UsageStatsSelection枚举类:
public enum UsageStatsSelection {
HOURLY(
UsageStatsManager.INTERVAL_DAILY,
System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1L),
System.currentTimeMillis()
),
DAILY(
UsageStatsManager.INTERVAL_DAILY,
0L,
System.currentTimeMillis()
),
WEEKLY(
UsageStatsManager.INTERVAL_WEEKLY,
0L,
System.currentTimeMillis()
),
MONTHLY(
UsageStatsManager.INTERVAL_MONTHLY,
0L,
System.currentTimeMillis()
),
YEARLY(
UsageStatsManager.INTERVAL_YEARLY,
0L,
System.currentTimeMillis()
);
private final int usageStatsInterval;
private final long beginTime;
private final long endTime;
UsageStatsSelection(int usageStatsInterval, long beginTime, long endTime) {
this.usageStatsInterval = usageStatsInterval;
this.beginTime = beginTime;
this.endTime = endTime;
}
public int getUsageStatsInterval() {
return usageStatsInterval;
}
public long getBeginTime() {
return beginTime;
}
public long getEndTime() {
return endTime;
}
}
由于无法获取queryEvents的所有日期(请参见:queryEvents(), 查询给定时间范围内的事件。系统仅保留几天的事件),因此我们将使用queryEvents仅获取每日事件。我们还将使用queryUsageStats()函数获取每周、每月和每年的使用数据。
@NonNull
public static Map<String, AppUsageStats> queryUsageStats(
Context context,
@NonNull UsageStatsSelection statsSelection
) {
final UsageStatsManager usageStatsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
final Map<String, AppUsageStats> appUsageStatsHashMap = new HashMap<>();
switch (statsSelection) {
case HOURLY:
final UsageEvents events = usageStatsManager.queryEvents(
statsSelection.getBeginTime(),
statsSelection.getEndTime()
);
Map<String, List<UsageEvents.Event>> eventsMap = new HashMap<>();
UsageEvents.Event currentEvent;
while (events.hasNextEvent()) {
currentEvent = new UsageEvents.Event();
if (events.getNextEvent(currentEvent)) {
switch (currentEvent.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
case UsageEvents.Event.ACTIVITY_PAUSED:
case UsageEvents.Event.ACTIVITY_STOPPED:
case UsageEvents.Event.FOREGROUND_SERVICE_START:
case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
List<UsageEvents.Event> packageEvents = eventsMap.get(currentEvent.getPackageName());
if (packageEvents == null) {
packageEvents = new ArrayList<>(Collections.singletonList(currentEvent));
} else {
packageEvents.add(currentEvent);
}
eventsMap.put(currentEvent.getPackageName(), packageEvents);
break;
}
}
}
for (Map.Entry<String, List<UsageEvents.Event>> entry : eventsMap.entrySet()) {
final AppUsageStatsBucket foregroundBucket = new AppUsageStatsBucket();
final Map<String, AppUsageStatsBucket> backgroundBucketMap = new HashMap<>();
for (int pos = 0; pos < entry.getValue().size(); pos++) {
final UsageEvents.Event event = entry.getValue().get(pos);
AppUsageStatsBucket backgroundBucket = backgroundBucketMap.get(event.getClassName());
if (backgroundBucket == null) {
backgroundBucket = new AppUsageStatsBucket();
backgroundBucketMap.put(event.getClassName(), backgroundBucket);
}
switch (event.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
foregroundBucket.setStartMillis(event.getTimeStamp());
break;
case UsageEvents.Event.ACTIVITY_PAUSED:
case UsageEvents.Event.ACTIVITY_STOPPED:
if (foregroundBucket.getStartMillis() >= foregroundBucket.getEndMillis()) {
if (foregroundBucket.getStartMillis() == 0L) {
foregroundBucket.setStartMillis(statsSelection.getBeginTime());
}
foregroundBucket.setEndMillis(event.getTimeStamp());
foregroundBucket.addTotalTime();
}
break;
case UsageEvents.Event.FOREGROUND_SERVICE_START:
backgroundBucket.setStartMillis(event.getTimeStamp());
break;
case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
if (backgroundBucket.getStartMillis() >= backgroundBucket.getEndMillis()) {
if (backgroundBucket.getStartMillis() == 0L) {
backgroundBucket.setStartMillis(statsSelection.getBeginTime());
}
backgroundBucket.setEndMillis(event.getTimeStamp());
backgroundBucket.addTotalTime();
}
break;
}
if (pos == entry.getValue().size() - 1) {
if (foregroundBucket.getStartMillis() > foregroundBucket.getEndMillis()) {
foregroundBucket.setEndMillis(statsSelection.getEndTime());
foregroundBucket.addTotalTime();
}
if (backgroundBucket.getStartMillis() > backgroundBucket.getEndMillis()) {
backgroundBucket.setEndMillis(statsSelection.getEndTime());
backgroundBucket.addTotalTime();
}
}
}
final long foregroundEnd = foregroundBucket.getEndMillis();
final long totalTimeForeground = foregroundBucket.getTotalTime();
final long backgroundEnd = backgroundBucketMap.values()
.stream()
.mapToLong(AppUsageStatsBucket::getEndMillis)
.max()
.orElse(0L);
final long totalTimeBackground = backgroundBucketMap.values()
.stream()
.mapToLong(AppUsageStatsBucket::getTotalTime)
.sum();
appUsageStatsHashMap.put(entry.getKey(), new AppUsageStats(
Math.max(foregroundEnd, backgroundEnd),
totalTimeForeground,
backgroundEnd,
totalTimeBackground
));
}
break;
default:
final List<UsageStats> usageStats = usageStatsManager
.queryUsageStats(
statsSelection.getUsageStatsInterval(),
statsSelection.getBeginTime(),
statsSelection.getEndTime()
);
appUsageStatsHashMap.putAll(usageStats.parallelStream()
.collect(Collectors.toMap(
UsageStats::getPackageName,
stats -> new AppUsageStats(
stats.getLastTimeUsed(),
stats.getTotalTimeInForeground(),
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? stats.getLastTimeForegroundServiceUsed()
: 0,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? stats.getTotalTimeForegroundServiceUsed()
: 0
),
(oldValue, newValue) -> newValue
)));
break;
}
return appUsageStatsHashMap;
}
结果将以包名和AppUsageStats列表的形式返回。