You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android运行时如何获取前台应用?能否监听前台应用切换及所需权限?

如何在Android中检测前台应用及接收切换事件?

嘿,关于这个问题我刚好有不少实践经验,给你梳理得明明白白:

一、怎么获取当前前台的应用?

不同Android版本的API限制不一样,毕竟Google一直在收紧隐私权限:

  • Android 5.0(API21)到Android10(API29):可以用UsageStatsManager来查询应用使用记录,或者老的ActivityManager.getRunningAppProcesses()(不过后者在API22后就被标记为废弃了,返回的结果也不一定准确,不推荐用)。
  • Android11+(API30及以上)UsageStatsManager是唯一可靠的途径,而且需要额外的权限才能用。

给你贴个UsageStatsManager的简单实现代码(Kotlin版):

val usageStatsManager = getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val endTime = System.currentTimeMillis()
val startTime = endTime - 1000 * 60 // 查询最近1分钟的应用使用记录
val stats = usageStatsManager.queryUsageStats(
    UsageStatsManager.INTERVAL_DAILY,
    startTime,
    endTime
)

var foregroundPackage: String? = null
var lastUsedTime = 0L
// 遍历找到最后使用的应用,也就是当前前台的
for (stat in stats) {
    if (stat.lastTimeUsed > lastUsedTime) {
        lastUsedTime = stat.lastTimeUsed
        foregroundPackage = stat.packageName
    }
}
// 拿到的foregroundPackage就是当前前台应用的包名啦

二、能在应用切换前台时收到事件通知吗?

当然可以,主要有两种方式,各有优劣:

  • 定时轮询:用Handler或者WorkManager定期调用上面的查询方法,对比上次的结果,如果包名变了就触发你的记录逻辑。这种方式简单易实现,但不够实时,而且会消耗一点系统资源。
  • 辅助服务(AccessibilityService):这是更实时的方案,通过辅助服务监听窗口状态变化。当有应用切换到前台时,会收到TYPE_WINDOW_STATE_CHANGED事件,直接就能拿到当前应用的包名。

给你说下辅助服务的实现步骤:

  1. 写一个继承AccessibilityService的类,重写事件处理方法:
class AppSwitchMonitorService : AccessibilityService() {
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
        // 监听窗口状态变化事件
        if (event?.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            val currentPackage = event.packageName?.toString()
            currentPackage?.let {
                // 这里处理应用切换的逻辑,比如把它添加到你的使用列表里
            }
        }
    }

    override fun onInterrupt() {
        // 服务被中断时的处理,一般空实现就行
    }
}
  1. AndroidManifest.xml里注册这个服务:
<service
    android:name=".AppSwitchMonitorService"
    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>
  1. 创建res/xml/accessibility_service_config.xml配置文件,指定要监听的事件:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true" />

⚠️ 注意:辅助服务需要用户手动在系统设置里开启,你得做个引导页面告诉用户怎么操作,没法自动申请权限。

三、需要哪些权限?

分两种情况来说:

  • 用UsageStatsManager的情况:需要android.permission.PACKAGE_USAGE_STATS权限。这个权限没法通过代码里的requestPermissions()申请,必须引导用户去系统设置的「应用使用权限」页面开启(一般路径是:设置 → 应用 → 你的应用 → 权限 → 应用使用情况)。
  • 用AccessibilityService的情况:需要android.permission.BIND_ACCESSIBILITY_SERVICE权限,这个权限在注册服务的时候声明就行,用户开启辅助服务后自动获取。
  • 还有个老权限android.permission.GET_TASKS,在API21之前能用,但现在已经被废弃了,高版本系统普通应用根本拿不到,别用了。

四、普通应用能实现吗?还是只能系统应用?

  • 普通应用:完全可以实现,但有两个小限制:
    1. 必须引导用户手动开启对应的权限(应用使用权限或辅助服务),没法静默获取。
    2. 在Android12+(API31)里,UsageStatsManager的查询结果可能会有一点延迟,而且某些系统应用的记录会被隐藏。
  • 系统应用:如果你的应用是预装在系统里、有系统签名的,那就能跳过一些权限申请步骤,比如直接拿到PACKAGE_USAGE_STATS权限,而且获取前台应用的信息更稳定,没有普通应用的那些限制。

内容的提问来源于stack exchange,提问作者The Matrix

火山引擎 最新活动