1. 前台服务概述与适用场景

定义与核心特征

前台服务(Foreground Service)是 Android 中一种持续运行的组件,通过一个不可清除的 系统通知(Notification) 向用户明确告知当前正在执行的任务。与后台服务(Background Service)相比,前台服务拥有更高的进程优先级,不会被系统轻易杀死,适用于需要长期在后台运行且对用户可见的场景。

对比维度前台服务后台服务
通知要求必须绑定一个持续显示的通知无需通知
进程优先级高(与可见 Activity 同级)低(容易被系统回收)
启动方式调用 startForegroundService() 并在 5 秒内调用 startForeground()调用 startService()
适用场景用户可感知的长期任务短暂或低优后台任务
Android 8.0+ 限制仍可自由启动后台执行受限需使用 JobScheduler

典型使用场景

  1. 音乐 / 音频播放:播放器需保持进程存活,即使用户切到其他应用。通知显示歌曲信息、控制按钮(播放/暂停/下一首)。
  2. 位置追踪(GPS):运动记录、导航、打车应用需持续获取位置更新,前台服务确保后台定位不会被系统中断。
  3. 文件上传 / 下载:大文件传输(如备份、更新包)需要可靠传输进度,前台服务配合通知展示进度条。

前台服务与后台服务的生命周期对比

下表列出关键生命周期方法及调用时机:

方法前台服务后台服务
onCreate()服务首次创建时调用同左
onStartCommand()收到启动意图后调用,必须在此方法内调用 startForeground()直接执行任务,无通知绑定
startForeground()将服务提升为前台状态并显示通知不可调用
stopForeground()移除前台状态(可移除或保留通知)不存在
onDestroy()服务终止前清理同左

前台服务启动与停止的核心流程

下面是一个标准的前台服务启动代码骨架(音乐播放示例):

// 启动前台服务
Intent intent = new Intent(context, MusicPlayerService.class);
ContextCompat.startForegroundService(context, intent);

// 在服务的 onStartCommand() 中绑定通知
public int onStartCommand(Intent intent, int flags, int startId) {
    Notification notification = buildNotification();
    startForeground(NOTIFICATION_ID, notification);
    // 开始执行播放逻辑
    return START_STICKY;
}

// 停止前台服务
private void stopService() {
    stopForeground(true);    // 移除通知
    stopSelf();              // 停止服务
}
⚠️ 重要约束:Android 8.0(API 26)起,调用 startForegroundService() 后必须在 5 秒内 调用 startForeground(),否则系统会抛出 ForegroundServiceDidNotStartInTimeException 并导致崩溃。该超时时间在 Android 12+ 中调整为 10 秒。

前台服务与后台服务的生命周期对比图

前台服务生命周期流程 startForegroundService() onCreate() onStartCommand() startForeground() 服务运行中(前台) stopForeground() stopSelf() onDestroy() 关键:前台服务必须通过 startForeground() 绑定通知,否则会在 5~10 秒内崩溃

适用场景选择决策要点

选择前台服务前必须确认:
1. 任务需要长时间运行(超过几分钟)?
2. 用户能够通过通知明确感知任务存在?
3. 如果被系统杀死,会造成明显的数据丢失或不良体验?
若以上三点全部为"是",则前台服务是正确选择;否则应考虑 WorkManager 或 JobScheduler。

典型场景 Notification 结构示例

以位置追踪为例,前台服务通知应包含持续动作指示:

Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle("位置追踪中")
    .setContentText("正在获取实时位置...")
    .setSmallIcon(R.drawable.ic_location)
    .setOngoing(true)                                  // 不可滑动清除
    .setPriority(NotificationCompat.PRIORITY_LOW)      // 不产生声音
    .addAction(R.drawable.ic_stop, "停止", stopPendingIntent)
    .build();
startForeground(LOCATION_NOTIFICATION_ID, notification);
💡 最佳实践:音乐播放前台服务建议使用 MediaStyle 来展示大样式通知及媒体控制按钮;文件上传则使用 ProgressNotification 显示进度百分比和预估剩余时间。