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

Android后台服务检测当前运行应用方案(适配Android10+)

问题原因

你当前使用的ActivityManager.getRunningAppProcesses()接口从Android 5.0开始就被逐步收紧调用权限,Android 10(API 29)及以上版本中,当应用处于后台状态时,调用该接口仅能返回应用自身的进程信息,无法获取其他第三方应用的运行状态,这是系统出于隐私保护做的强制限制,不存在绕开权限的黑科技,必须使用官方公开的合规API实现需求。

实现方案

按实现成本和兼容性从高到低排序:

方案一:监听系统屏保广播(最轻量,优先推荐)

所有合规实现的屏保应用(包括系统原生屏保、第三方屏保)在启动/退出时,系统都会发送对应广播,Android 6.0(API 23)及以上版本可直接动态注册广播接收器监听,不需要申请特殊权限,无额外功耗,不需要轮询:

  1. 注册广播的代码:
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.DREAMING_STARTED"); // 屏保启动
filter.addAction("android.intent.action.DREAMING_STOPPED"); // 屏保退出
context.registerReceiver(new ScreenSaverReceiver(), filter);
  1. 广播接收器实现:
public class ScreenSaverReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if ("android.intent.action.DREAMING_STARTED".equals(action)) {
            // 屏保正在运行,按业务逻辑判断是否需要启动自身应用
        } else if ("android.intent.action.DREAMING_STOPPED".equals(action)) {
            // 屏保已退出
        }
    }
}

注意:部分不遵循Android开发规范的第三方屏保应用可能不会触发该广播,这类场景需要搭配下面的前台应用检测方案兜底。

方案二:UsageStatsManager获取前台应用(全场景兼容)

这是目前普通应用检测前台应用兼容性最好的方案,支持Android 5.0到最新版本,仅需要用户手动授权使用情况访问权限即可:

  1. 在AndroidManifest.xml中声明特殊权限:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
  1. 检测权限是否已授权的方法:
public static boolean checkUsagePermission(Context context) {
    AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    int mode;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        mode = appOps.unsafeCheckOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                Process.myUid(), context.getPackageName());
    } else {
        mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                Process.myUid(), context.getPackageName());
    }
    return mode == AppOpsManager.MODE_ALLOWED;
}
  1. 权限未授权时,引导用户跳转到设置页开启:
Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(intent);
  1. 获取当前前台应用包名的实现:
public static String getCurrentForegroundPackage(Context context) {
    UsageStatsManager usm = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
    long now = System.currentTimeMillis();
    // 查询最近10秒内的应用使用记录,保证数据时效性
    List<UsageStats> stats = usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, now - 10000, now);
    if (stats == null || stats.isEmpty()) return "";
    UsageStats latestStats = null;
    for (UsageStats s : stats) {
        if (latestStats == null || s.getLastTimeUsed() > latestStats.getLastTimeUsed()) {
            latestStats = s;
        }
    }
    return latestStats != null ? latestStats.getPackageName() : "";
}

拿到前台包名后,你可以通过两种方式判断是否为屏保应用:

  • 检查目标应用是否包含BIND_DREAM_SERVICE权限,所有正规屏保应用都必须实现DreamService,会自动持有该权限
  • 提前收集需要识别的屏保应用包名,做名单匹配

方案三:无障碍服务获取前台应用

如果你的应用本身已经在使用无障碍服务,可以直接通过无障碍事件回调获取前台应用包名,不需要额外申请使用情况访问权限:

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
            && event.getPackageName() != null) {
        String foregroundPkg = event.getPackageName().toString();
        // 在此处判断是否为屏保应用即可
    }
}

该方案的缺点是需要用户手动开启应用的无障碍权限,用户接受度较低。

避坑提示
  • Android 12(API 31)及以上版本对后台启动Activity做了严格限制,就算检测到屏保运行,也不能直接从后台拉起自身应用,必须满足系统规定的后台启动豁免条件(比如持有悬浮窗权限、用户刚刚点击过应用通知、应用为默认桌面/默认助手等),否则启动请求会被系统直接拦截。
  • 不要高频轮询前台应用状态,检测间隔建议不低于15秒,否则会被系统判定为异常耗电,强制杀死后台服务。
  • 后台服务建议使用前台服务实现,同时做好保活逻辑,避免长时间后台运行被系统回收。

内容的提问来源于stack exchange,提问作者Mohammed Asim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:45:42