每隔X秒调用方法获取UsageStats始终相同的问题求助
问题解决:定时获取UsageStat数据始终不变的问题
核心原因
你遇到的问题本质是**UsageStatsManager.queryUsageStats()返回的是系统缓存的历史统计数据,并非实时更新**。系统通常会间隔1小时左右才会刷新UsageStats数据集,短时间内重复查询只会拿到相同的缓存值,清空本地map无法改变数据源本身的更新延迟。
另外,你当前的查询范围是过去5天到当前时间,这种方式适合统计历史总时长,而非实时监控应用前台使用情况。
解决方案
方案1:用queryEvents实时统计前台时长(推荐)
如果需要实时获取应用前台时长,应该监听系统的UsageEvents事件,通过记录应用进入/退出前台的时间点来计算实时时长,示例代码如下:
private void trackRealTimeForeground() { UsageStatsManager usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); long now = System.currentTimeMillis(); // 查询最近10秒的事件,可根据需求调整 UsageEvents events = usm.queryEvents(now - 10000, now); UsageEvents.Event event = new UsageEvents.Event(); HashMap<String, Long> appForegroundStart = new HashMap<>(); while (events.hasNextEvent()) { events.getNextEvent(event); String pkgName = event.getPackageName(); switch (event.getEventType()) { case UsageEvents.Event.MOVE_TO_FOREGROUND: // 记录应用进入前台的时间 appForegroundStart.put(pkgName, event.getTimeStamp()); break; case UsageEvents.Event.MOVE_TO_BACKGROUND: // 计算本次前台时长并累加 Long startTime = appForegroundStart.get(pkgName); if (startTime != null) { long duration = event.getTimeStamp() - startTime; // 更新该应用的总前台时长 // 例如:totalForegroundTime.put(pkgName, totalForegroundTime.getOrDefault(pkgName, 0L) + duration); } appForegroundStart.remove(pkgName); break; } } }
方案2:优化queryUsageStats的使用方式(适合非实时统计)
如果必须依赖queryUsageStats,可以调整查询逻辑适配系统的更新频率:
- 缩小查询时间范围,比如只查询最近1小时的数据,而非过去5天
- 延长查询间隔至30分钟以上,匹配系统的缓存刷新周期
- 尝试调用
usm.clearCachedData()(需特定权限,部分系统不支持)强制刷新缓存
代码优化建议
- 避免重复创建定时任务:当前在
onStartCommand中创建Handler,会导致Service重启时出现多个并行的定时任务,建议改用JobScheduler或全局单例Handler。 - 简化变量作用域:
stats、mPackageStats等变量可以在pushAppList内声明为局部变量,减少全局内存占用。 - 权限校验:确保已申请
android.permission.PACKAGE_USAGE_STATS权限,且用户在系统设置中授予了应用使用统计权限。
原问题代码
public class PushService extends Service { private static final int NOTIF_ID = 1; private static final String NOTIF_CHANNEL_ID = "Channel_Id_1"; private static final String NOTIF_CHANNEL_ID2 = "Channel_Id_2"; private static final int _DISPLAY_ORDER_USAGE_TIME = 0; private static final int _DISPLAY_ORDER_LAST_TIME_USED = 1; private static final int _DISPLAY_ORDER_APP_NAME = 2; private static final boolean localLOGV = false; private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME; private AppNameComparator mAppLabelComparator; private LastTimeUsedComparator mLastTimeUsedComparator; private UsageTimeComparator mUsageTimeComparator; private PackageManager mPm; private UsageStatsManager mUsageStatsManager; public HashMap<String, Long> mapToFirebase = new HashMap<>();; public ArrayList<UsageStats> mPackageStats; public ArrayMap<String, String> mAppLabelMap; public List<UsageStats> stats; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId){ /*notice*/ startForeground(); if (intent != null){ String uid = intent.getStringExtra("parentUID"); if (!uid.equals("")){ Toast.makeText(this, uid, Toast.LENGTH_SHORT).show(); Handler ha=new Handler(); ha.postDelayed(new Runnable() { @Override public void run() { //call function pushAppList(); mapToFirebase.clear(); loadDataToMapForFirebase(); Log.d("mapservice", mapToFirebase.toString()); ha.postDelayed(this, 5000); } }, 10000); } } return super.onStartCommand(intent, flags, startId); } public static class AppNameComparator implements Comparator<UsageStats> { private Map<String, String> mAppLabelList; AppNameComparator(Map<String, String> appList) { mAppLabelList = appList; } @Override public final int compare(UsageStats a, UsageStats b) { String alabel = mAppLabelList.get(a.getPackageName()); String blabel = mAppLabelList.get(b.getPackageName()); return alabel.compareTo(blabel); } } public void pushAppList(){ mAppLabelMap = new ArrayMap<>(); mPackageStats = new ArrayList<>(); mPm = getPackageManager(); mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_YEAR, -5); mLastTimeUsedComparator = new LastTimeUsedComparator(); mUsageTimeComparator = new UsageTimeComparator(); stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, cal.getTimeInMillis(), System.currentTimeMillis()); if (stats == null) { return; } ArrayMap<String, UsageStats> map = new ArrayMap<>(); int statCount = stats.size(); for (int i = 0; i < statCount; i++) { Log.d("", String.valueOf(stats.get(i).getTotalTimeInForeground())); android.app.usage.UsageStats pkgStats = stats.get(i); // load application labels for each application try { ApplicationInfo appInfo = mPm.getApplicationInfo(pkgStats.getPackageName(), 0); String label = appInfo.loadLabel(mPm).toString(); mAppLabelMap.put(pkgStats.getPackageName(), label); UsageStats existingStats = map.get(pkgStats.getPackageName()); if (existingStats == null) { map.put(pkgStats.getPackageName(), pkgStats); } else { existingStats.add(pkgStats); } } catch (PackageManager.NameNotFoundException e) { // This package may be gone. } } mPackageStats.addAll(map.values()); // Sort list mAppLabelComparator = new AppNameComparator(mAppLabelMap); sortList(); } public static class LastTimeUsedComparator implements Comparator<UsageStats> { @Override public final int compare(UsageStats a, UsageStats b) { // return by descending order return (int)(b.getLastTimeUsed() - a.getLastTimeUsed()); } } public static class UsageTimeComparator implements Comparator<UsageStats> { @Override public final int compare(UsageStats a, UsageStats b) { return (int)(b.getTotalTimeInForeground() - a.getTotalTimeInForeground()); } } private void sortList() { if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) { if (localLOGV) Log.i(TAG, "Sorting by usage time"); Collections.sort(mPackageStats, mUsageTimeComparator); } else if (mDisplayOrder == _DISPLAY_ORDER_LAST_TIME_USED) { if (localLOGV) Log.i(TAG, "Sorting by last time used"); Collections.sort(mPackageStats, mLastTimeUsedComparator); } else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) { if (localLOGV) Log.i(TAG, "Sorting by application name"); Collections.sort(mPackageStats, mAppLabelComparator); } } public void loadDataToMapForFirebase(){ /*TODO UPDATE DATA ONBACKGROUND*/ String system_sign = "android"; PackageManager pm = getPackageManager(); if (mPackageStats.size() != 0){ for (int i = 0; i < mPackageStats.size(); i++){ if (pm.checkSignatures(system_sign, mPackageStats.get(i).getPackageName()) == PackageManager.SIGNATURE_MATCH) { Log.d("CheckAppSys", mPackageStats.get(i).getPackageName() + " is sys."); } else { String usagetime = DateUtils.formatElapsedTime(mPackageStats.get(i).getTotalTimeInForeground() / 1000); Pattern special= Pattern.compile("[^a-z0-9 ]", Pattern.CASE_INSENSITIVE); Matcher matcher = special.matcher(Objects.requireNonNull(mAppLabelMap.get(mPackageStats.get(i).getPackageName()))); boolean constainsSymbols = matcher.find(); if(!constainsSymbols){ mapToFirebase.put(mAppLabelMap.get(mPackageStats.get(i).getPackageName()), mPackageStats.get(i).getTotalTimeInForeground()); } Log.d("maptofirebase", mapToFirebase.toString()); Log.d("CheckApp", mPackageStats.get(i).getPackageName() + " is not sys."); } } } } private void startForeground() { Intent notificationIntent = new Intent(this, UsageStatsActivity.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel1 = new NotificationChannel( NOTIF_CHANNEL_ID, "Channel 1", NotificationManager.IMPORTANCE_HIGH ); channel1.setDescription("This is channel 1"); NotificationChannel channel2 = new NotificationChannel( NOTIF_CHANNEL_ID2, "Channel 2", NotificationManager.IMPORTANCE_LOW ); channel2.setDescription("This is channel 2"); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel1); manager.createNotificationChannel(channel2); } PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); startForeground(NOTIF_ID, new NotificationCompat.Builder(this, NOTIF_CHANNEL_ID) // don't forget create a notification channel first .setOngoing(true) .setSmallIcon(R.drawable.logo) .setContentTitle(getString(R.string.app_name)) .setContentText("Monitoring apps.") .setContentIntent(pendingIntent) .build()); } }
内容的提问来源于stack exchange,提问作者Mr Learning
相关产品推荐
相关产品推荐

