3. 启动前台服务:Service.startForeground()方法、通知构建与权限要求

说实话,写一个后台服务不难,但把它变成前台服务——嗯,这里面的坑比想象中多。我刚入行那会儿,以为前台服务不就是调一下 startForeground() 嘛?结果第一次上线的 App 在 Android 12 上直接闪退……后来才明白,通知不行、权限不对,系统真会直接拿你开刀。

这一节咱们就掰开揉碎讲清楚:怎么启动一个合法的前台服务,通知怎么构建才不会被系统扇耳光,以及各种 Android 版本下的权限要求。你想想看,如果你的服务连前台都算不上,那后面监控 CPU 帧率压根就没法聊。

3.1 startForeground() 的调用时机与签名

先看方法签名,很简单:

public final void startForeground(int id, Notification notification)

两个参数:

  • id:通知的唯一标识,不能为 0。我习惯用 System.currentTimeMillis() 转 int,或者直接固定一个常量如 1001。
  • notification:一个构造好的 Notification 对象,必须包含渠道(Android 8.0+ 强制)。
⚠ 注意: 这个方法必须在 onStartCommand() 或 onCreate() 中调用,而且必须在服务启动后的几秒内完成。如果超时未调用,系统会认为你的服务是“非法前台服务”,直接杀掉——我在小米手机上遇到过,血泪教训。

3.2 通知渠道:Android 8.0 的强制门槛

Android 8.0(API 26)开始,所有通知都必须绑定一个通知渠道(NotificationChannel)。前台服务通知也不例外。没渠道?通知发不出来,startForeground() 直接抛异常。

我个人习惯在 Application 的 onCreate() 里提前创建好渠道:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        CHANNEL_ID_FOREGROUND,
        "前台服务通知",
        NotificationManager.IMPORTANCE_LOW   // 前台服务通知建议用 LOW,不打扰用户
    ).apply {
        description = "用于显示前台服务的持续运行状态"
    }
    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    manager.createNotificationChannel(channel)
}

注意优先级选 IMPORTANCE_LOW 或 IMPORTANCE_MIN——前台服务是常驻通知,闹钟似的高优先级会让用户暴躁。我曾经有个项目用了 IMPORTANCE_HIGH,结果被用户骂到改版。

3.3 通知构建:一个“合格”的前台服务通知长什么样?

光有渠道还不够,通知本身也得有“身份感”。Android 13 之后系统会更严格检查通知的内容和样式。我总结了一个“三件套”最低要求:

  1. 标题和正文:不能为空。哪怕写“正在运行”也行,但不能空着。
  2. 小图标:自定义一个白色图标的 drawable,不要用系统默认的。
  3. 点击行为:最好有个 PendingIntent 跳回应用主页,方便用户关闭服务。

代码示例:

val notification = NotificationCompat.Builder(this, CHANNEL_ID_FOREGROUND)
    .setContentTitle("CPU监控服务")
    .setContentText("正在采集帧率数据...")
    .setSmallIcon(R.drawable.ic_monitor)
    .setContentIntent(pendingIntent)  // 点击回到 Activity
    .setOngoing(true)                 // 用户不能侧滑清除
    .setSilent(true)                  // 不播放声音
    .build()

startForeground(NOTIFICATION_ID, notification)
💡 技巧: setOngoing(true) 是前台服务通知的标配。如果你忘了设,用户长按“停止”后服务可能会被杀——我亲测过,真的会发生。

3.4 权限要求:从 Android 9 到 Android 14 的进化史

前台服务权限不是一直都有,而是 Android 9(API 28)之后逐步收紧的。我整理了一张表,你对照项目里的 minSdkVersion 检查:

Android 版本所需权限备注
8.0 - 8.1(API 26-27)无特殊前台权限,但需通知渠道直接 startForeground() 即可
9.0(API 28)FOREGROUND_SERVICE必须声明,否则运行时崩溃
10.0 - 12.0(API 29-31)FOREGROUND_SERVICE + 部分类型需要额外权限如 location 等特殊类型需动态申请
13.0(API 33)FOREGROUND_SERVICE + POST_NOTIFICATIONS通知权限必须动态弹窗申请
14.0(API 34)同上+FOREGROUND_SERVICE_TYPE_* 必须指定前台服务类型必须在 manifest 中列出

看看,是不是越来越复杂?我遇到过最坑的就是 Android 13 上忘记加 POST_NOTIFICATIONS 权限,结果 startForeground() 直接抛 SecurityException,而且日志里只有一行“Permission denied”……排查了两小时才找到原因。

3.5 前台服务类型:Android 14 必须明确声明

从 Android 12 开始,Google 要求前台服务必须在 Manifest 中指定 android:foregroundServiceType。Android 14 更严格——类型不写,startForeground() 直接失败。

我们的 CPU 帧率监控服务,本质上属于“数据处理”或“特殊用途”。但注意,没有直接的“CPU监控”类型。我一般用 dataSync 或 shortService,但更合理的做法是声明 specialUse 并配上说明:

<service
    android:name=".ForegroundMonitorService"
    android:foregroundServiceType="dataSync|specialUse"
    android:exported="false" />
📌 关键点: 如果你的服务涉及多个类型,用 | 分隔。但注意,specialUse 需要提交 Google 审核才能上架——所以一般项目里用 dataSync 就够了,除非你的监控确实是“特殊用途”。

3.6 完整启动流程:一张图说清

下面这张图把整个前台服务启动的步骤串起来了。从创建通知渠道到调用 startForeground(),每一步出错都可能崩。

前台服务启动流程图 1. 创建通知渠道 2. 构建Notification对象 3. 检查FOREGROUND_SERVICE权限 4. 检查POST_NOTIFICATIONS(API 33+) 5. 检查foregroundServiceType声明 6. 调用startForeground() 权限不足→SecurityException 通知异常→throw异常

你看,在绿色节点调用之前,有四个前置检查。任何一个漏掉,你的服务就启动不了。这就是为什么很多新手觉得“我就是调了个 startForeground 怎么报错了”——其实前面缺了一堆配置。

3.7 常见坑与避坑指南

这里列几个我真实踩过的坑:

  • 坑1:在服务还没绑定通知渠道时就调用 startForeground()——Android 8.0+ 会直接崩溃。解决办法是在 Application 初始化时就创建渠道。
  • 坑2:前台服务通知的 SmallIcon 用了彩色图——Android 系统会自动把彩色图标压成白色,但有些 ROM 会显示异常。我建议统一用白色透明底。
  • 坑3:Android 13+ 忘记请求 POST_NOTIFICATIONS 权限——这个权限是运行时权限,需要在 startForeground() 之前弹窗请求。我曾经在一个库项目里忘了加,测试手机直接闪退,日志一时半会没看出来。
  • 坑4:多个前台服务共用同一个通知 ID——如果你不传入唯一 ID,后启动的服务会覆盖前一个通知,导致系统误以为服务停止。我习惯用随机数生成 ID。
🛠 个人建议: 写一个工具方法封装 startForegroundService(),在里面统一处理权限检查、渠道创建、通知构建。这样所有前台服务都走同一套逻辑,不容易遗漏。我就是这么干的。

3.8 小结这节课的核心

启动前台服务不是一行代码的事。你得准备:

  • 通知渠道(API 26+)
  • 通知对象(必须有标题、图标、点击行为)
  • 权限声明(FOREGROUND_SERVICE 是底线,API 33+ 要 POST_NOTIFICATIONS)
  • 前台服务类型(API 34+ 必须写)