如何获取Android应用的实时使用时长统计
解决Android应用实时获取当日使用时长的问题
问题根源
系统的UsageStatsManager不会实时更新应用的使用时长数据,它是周期性批量同步统计结果的。所以当应用在前台运行时,直接调用queryUsageStats获取的是上次同步的数值,不会实时增长,只有当应用切换前后台或系统完成同步后,数值才会更新。
解决方案
方案一:自行监听应用前后台状态,实时统计时长
通过监听应用的Activity生命周期,判断应用是否处于前台,记录进入前台的时间,实时计算当前前台运行时长,再结合UsageStats的历史数据得到当日总时长,这种方式实时性最强。
步骤1:自定义Application类,注册生命周期回调
public class MyApp extends Application { private boolean isAppForeground = false; private long foregroundStartTime = 0; private long totalForegroundTime = 0; // 本次启动以来累计的前台时长 @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @Override public void onActivityStarted(@NonNull Activity activity) { // 应用从后台切到前台时,记录起始时间 if (!isAppForeground) { isAppForeground = true; foregroundStartTime = System.currentTimeMillis(); } } @Override public void onActivityResumed(@NonNull Activity activity) {} @Override public void onActivityPaused(@NonNull Activity activity) {} @Override public void onActivityStopped(@NonNull Activity activity) { // 确认所有Activity都进入后台,才标记应用退到后台 if (isAppForeground && !isAnyActivityVisible()) { isAppForeground = false; totalForegroundTime += System.currentTimeMillis() - foregroundStartTime; } } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} @Override public void onActivityDestroyed(@NonNull Activity activity) {} }); } // 判断当前是否有本应用的Activity处于前台 private boolean isAnyActivityVisible() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); if (!tasks.isEmpty()) { ComponentName topActivity = tasks.get(0).topActivity; return topActivity.getPackageName().equals(getPackageName()); } return false; } // 获取当日总使用时长:历史统计时长 + 本次累计时长 + 当前前台运行时长 public long getTodayTotalUsageTime() { long currentTime = System.currentTimeMillis(); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); long dayStart = calendar.getTimeInMillis(); // 从UsageStats获取当日历史使用时长 UsageStatsManager usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); List<UsageStats> statsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, dayStart, currentTime); long historicalTime = 0; for (UsageStats stats : statsList) { if (stats.getPackageName().equals(getPackageName())) { historicalTime = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? stats.getTotalTimeVisible() : stats.getTotalTimeInForeground(); break; } } // 计算当前正在前台运行的时长 long currentForegroundTime = isAppForeground ? System.currentTimeMillis() - foregroundStartTime : 0; return historicalTime + totalForegroundTime + currentForegroundTime; } }
步骤2:在MainActivity中调用统计方法
Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { long totalUsage = ((MyApp) getApplication()).getTodayTotalUsageTime(); Log.d("MainActivity", "Usage time: " + totalUsage); handler.postDelayed(this, 1000); } });
方案二:通过UsageEvents获取细粒度状态事件
使用UsageStatsManager.queryEvents获取应用的前后台切换事件,通过事件时间戳计算实时时长,这种方式依赖系统事件记录,无需自行维护生命周期。
Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { long currentTime = System.currentTimeMillis(); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); long dayStart = calendar.getTimeInMillis(); UsageStatsManager usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); UsageEvents events = usm.queryEvents(dayStart, currentTime); long totalTime = 0; long lastForegroundTs = 0; boolean isForeground = false; UsageEvents.Event event = new UsageEvents.Event(); // 遍历所有事件,计算前台时长 while (events.hasNextEvent()) { events.getNextEvent(event); if (event.getPackageName().equals(getPackageName())) { if (event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) { lastForegroundTs = event.getTimeStamp(); isForeground = true; } else if (event.getEventType() == UsageEvents.Event.MOVE_TO_BACKGROUND) { if (isForeground) { totalTime += event.getTimeStamp() - lastForegroundTs; isForeground = false; } } } } // 如果当前应用在前台,加上从最后一次进入前台到现在的时长 if (isForeground) { totalTime += currentTime - lastForegroundTs; } Log.d("MainActivity", "Usage time: " + totalTime); handler.postDelayed(this, 1000); } });
注意事项
- 两种方案都需要申请
android.permission.PACKAGE_USAGE_STATS权限,并且需要引导用户在设置 > 应用 > 权限管理 > 使用权限访问中开启对应权限。 - Android 10及以上版本,应用在后台时
Handler可能被系统暂停,若需要后台持续统计,建议使用WorkManager或前台服务实现。 - 方案一的自行统计方式可靠性更高,实时性强;方案二依赖系统事件,可能存在少量延迟。
内容的提问来源于stack exchange,提问作者sarthak pawar
相关产品推荐
相关产品推荐

