1. 前台服务概述与适用场景
定义与核心特征
前台服务(Foreground Service)是 Android 中一种持续运行的组件,通过一个不可清除的 系统通知(Notification) 向用户明确告知当前正在执行的任务。与后台服务(Background Service)相比,前台服务拥有更高的进程优先级,不会被系统轻易杀死,适用于需要长期在后台运行且对用户可见的场景。
| 对比维度 | 前台服务 | 后台服务 |
|---|---|---|
| 通知要求 | 必须绑定一个持续显示的通知 | 无需通知 |
| 进程优先级 | 高(与可见 Activity 同级) | 低(容易被系统回收) |
| 启动方式 | 调用 startForegroundService() 并在 5 秒内调用 startForeground() | 调用 startService() |
| 适用场景 | 用户可感知的长期任务 | 短暂或低优后台任务 |
| Android 8.0+ 限制 | 仍可自由启动 | 后台执行受限需使用 JobScheduler |
典型使用场景
- 音乐 / 音频播放:播放器需保持进程存活,即使用户切到其他应用。通知显示歌曲信息、控制按钮(播放/暂停/下一首)。
- 位置追踪(GPS):运动记录、导航、打车应用需持续获取位置更新,前台服务确保后台定位不会被系统中断。
- 文件上传 / 下载:大文件传输(如备份、更新包)需要可靠传输进度,前台服务配合通知展示进度条。
前台服务与后台服务的生命周期对比
下表列出关键生命周期方法及调用时机:
| 方法 | 前台服务 | 后台服务 |
|---|---|---|
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 秒。
前台服务与后台服务的生命周期对比图
适用场景选择决策要点
选择前台服务前必须确认:
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 显示进度百分比和预估剩余时间。