API21弃用getRunningTasks后,如何获取前台应用及前台运行时长?
获取前台应用及其运行时长的替代方案(替代已弃用的
getRunningTasks) Hey there, I totally get it—losing the getRunningTasks() method in API 21 was such a letdown, since it made grabbing foreground app info so straightforward. Let's walk through the reliable alternatives we have now, plus how to track how long an app stays in the foreground.
方案1:UsageStatsManager(官方推荐,API 21+)
This is the official go-to replacement, though it requires user authorization. Here's how to set it up:
- Add permissions to your manifest:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
- Guide users to enable the permission: This is a special system permission, so you'll need to send users to the system settings to turn it on manually:
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) startActivity(intent)
- Fetch the foreground app package:
fun getForegroundAppPackageName(context: Context): String? { val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager val endTime = System.currentTimeMillis() val startTime = endTime - 1000 * 60 * 5 // 检查最近5分钟的使用记录 val usageStatsList = usageStatsManager.queryUsageStats( UsageStatsManager.INTERVAL_BEST, startTime, endTime ) // 最近一次被使用的应用即为当前前台应用 return usageStatsList.maxByOrNull { it.lastTimeUsed }?.packageName }
- 额外:获取前台时长:
UsageStats对象自带totalTimeInForeground字段,直接就能拿到该应用累计在前台的总时长,无需额外计算!
方案2:AccessibilityService(实时监控,API 18+)
如果需要持续监控前台应用的切换,这个方案很合适。它不需要应用使用统计权限,但用户需要开启你的应用辅助功能。
- 创建AccessibilityService子类:
class ForegroundAppMonitorService : AccessibilityService() { private var currentForegroundPackage: String? = null private var foregroundStartTime: Long = 0 override fun onAccessibilityEvent(event: AccessibilityEvent) { // 监听窗口状态变化(应用切换前台时触发) if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { event.packageName?.toString()?.let { newPackage -> if (currentForegroundPackage != newPackage) { // 计算上一个前台应用的运行时长 currentForegroundPackage?.let { oldPackage -> val duration = System.currentTimeMillis() - foregroundStartTime Log.d("ForegroundTrack", "$oldPackage 前台运行时长:${duration}ms") } // 更新当前应用及前台启动时间 currentForegroundPackage = newPackage foregroundStartTime = System.currentTimeMillis() } } } } override fun onInterrupt() { // 处理服务中断逻辑 } }
- 在Manifest中注册服务:
<service android:name=".ForegroundAppMonitorService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
- 创建辅助功能配置文件(res/xml/accessibility_service_config.xml):
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true" />
- 注意:用户需要在设备的辅助功能设置中手动开启你的服务。这个方案可以精准追踪每次应用切换的前台启动/结束时间,计算单会话时长。
方案3:AppOpsManager(API 23+,不推荐长期使用)
这是一个依赖隐藏API的 workaround,未来Android版本可能会失效。它只能检查指定应用是否在前台,无法直接获取当前前台应用。
fun isAppInForeground(context: Context, packageName: String): Boolean { val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager return try { val checkOpMethod = appOpsManager.javaClass.getMethod( "checkOpNoThrow", Integer.TYPE, Integer.TYPE, String::class.java ) // OP_ACTIVITY_RECORD 是隐藏常量,值为24 val mode = checkOpMethod.invoke(appOpsManager, 24, android.os.Process.myUid(), packageName) as Int mode == AppOpsManager.MODE_ALLOWED } catch (e: Exception) { e.printStackTrace() false } }
- 警告:由于依赖隐藏API,这不是一个稳定的长期解决方案,仅在其他方案不适用时考虑使用。
总结
- 单次查询前台应用+累计时长:用UsageStatsManager(官方推荐,稳定可靠)。
- 实时监控切换+精准单会话时长:用AccessibilityService。
- 尽量避免使用AppOpsManager,稳定性无法保障。
内容的提问来源于stack exchange,提问作者wednesdaymiko
相关产品推荐
相关产品推荐

