You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

每隔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()(需特定权限,部分系统不支持)强制刷新缓存

代码优化建议

  1. 避免重复创建定时任务:当前在onStartCommand中创建Handler,会导致Service重启时出现多个并行的定时任务,建议改用JobScheduler或全局单例Handler。
  2. 简化变量作用域:stats、mPackageStats等变量可以在pushAppList内声明为局部变量,减少全局内存占用。
  3. 权限校验:确保已申请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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:05:18