Android 后台服务 Foreground Service
认识前台服务(Foreground Service)
前台服务是 Android 中一种有感知的后台任务执行机制。与普通后台服务不同,前台服务必须通过持续可见的通知告知用户该服务正在运行,系统也因此赋予它更高的优先级,使其几乎不会被系统自动杀死。
常见的应用场景包括:
- 音乐播放器在后台播放音乐,通知栏显示播放控制
- 运动记录应用实时追踪跑步/骑行轨迹
- 文件下载管理器显示下载进度
- 视频通话应用在后台保持通话状态(配合悬浮窗)
前台服务与后台服务的区别
| 特性 | 前台服务 | 后台服务 |
|---|---|---|
| 用户感知 | 强制显示通知,用户知道有任务在运行 | 通常无界面,用户可能不知道 |
| 系统优先级 | 高,几乎不会被系统回收 | 低,内存紧张时容易被杀死 |
| 在 Android 8.0+ 后台限制 | 不受后台执行限制(但仍有某些约束) | 启动受限,无法长时间运行 |
| 典型用途 | 音乐、导航、运动、下载 | 数据同步、数据库清理、短期任务 |
实现前台服务的基础步骤
一个完整的前台服务需要完成以下五步:
- 创建服务类并继承
Service - 在
onStartCommand中构建通知,调用startForeground() - 在
AndroidManifest.xml中声明服务及权限 - 在需要的地方通过
startService()或startForegroundService()启动服务 - 任务完成后调用
stopForeground()或stopSelf()退出
第一步:创建服务类
public class MyForegroundService extends Service {
private static final String CHANNEL_ID = "foreground_service_channel";
private static final int NOTIFICATION_ID = 101;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 创建通知渠道(Android 8.0 及以上必须)
createNotificationChannel();
// 构建前台服务所需的通知
Notification notification = buildNotification();
// 启动前台服务
startForeground(NOTIFICATION_ID, notification);
// 在此处执行实际的后台任务(例如播放音乐、下载文件)
// ...
return START_STICKY; // 或 START_NOT_STICKY, START_REDELIVER_INTENT
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"前台服务",
NotificationManager.IMPORTANCE_LOW // 前台服务必须至少是 LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
if (manager != null) {
manager.createNotificationChannel(channel);
}
}
}
private Notification buildNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
);
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("前台服务正在运行")
.setContentText("点击返回应用")
.setSmallIcon(R.drawable.ic_notification) // 必须设置小图标
.setContentIntent(pendingIntent)
.setOngoing(true) // 表示这是一个持续通知,不能被用户滑动删除
.build();
}
@Override
public IBinder onBind(Intent intent) {
return null; // 如果不需绑定,直接返回 null
}
}
第二步:在 AndroidManifest.xml 中声明
<manifest ...>
<!-- Android 9+ 需要声明前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 如果需要特定类型的前台服务,还需声明对应权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ -->
<application ...>
<service
android:name=".MyForegroundService"
android:foregroundServiceType="mediaPlayback"
android:exported="false" />
</application>
</manifest>
注意:Android 10(API 29)及以上,在
startForeground()调用前必须显式声明android:foregroundServiceType,Android 14 起变得更加严格。请根据实际用途选择类型,如dataSync、location、mediaPlayback等。
第三步:启动前台服务
在 Activity 或其他组件中启动服务:
Intent serviceIntent = new Intent(this, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Android 8.0 及以上必须使用 startForegroundService()
startForegroundService(serviceIntent);
} else {
startService(serviceIntent);
}
第四步:停止前台服务
当任务完成或不再需要前台服务时,应当主动移除通知并停止服务:
// 在 Service 内部调用
stopForeground(true); // 参数 true 表示同时移除通知
stopSelf();
如果只是想让服务转为后台服务(不再需要高优先级),可以只调用 stopForeground(false),这仅移除前台状态和通知,但不停止服务本身。
前台服务的类型与新增权限(Android 14+ 变化)
从 Android 14 开始,每当前台服务启动时,系统会严格要求声明服务类型,且每种类型对应单独的权限。缺失权限会导致 SecurityException。
常见的前台服务类型及所需权限:
| 服务类型 | 用途 | 所需权限(Android 14+) |
|---|---|---|
dataSync |
数据同步 | FOREGROUND_SERVICE_DATA_SYNC |
location |
定位跟踪 | FOREGROUND_SERVICE_LOCATION |
mediaPlayback |
音频/视频播放 | FOREGROUND_SERVICE_MEDIA_PLAYBACK |
mediaProjection |
屏幕录制/投屏 | FOREGROUND_SERVICE_MEDIA_PROJECTION |
camera |
相机使用 | FOREGROUND_SERVICE_CAMERA |
phoneCall |
通话服务 | FOREGROUND_SERVICE_PHONE_CALL |
health |
健康/运动数据 | 需要申请运行时权限 HIGH_SAMPLING_RATE_SENSORS,并在 Android 15+ 必须声明 health 类型 |
在 AndroidManifest.xml 中必须精确配置:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<service
android:name=".LocationForegroundService"
android:foregroundServiceType="location"
android:exported="false" />
如果服务用到多种类型,可以用 | 分隔:android:foregroundServiceType="location|dataSync",并请求所有对应权限。
通知的设计要求
前台通知是用户与服务的核心交互入口,设计时需注意:
- 强制不可取消:必须调用
.setOngoing(true),否则用户滑动删除通知可能导致服务被系统杀死(Android 8.0+ 允许几秒内重建,但不保证)。 - 必须包含小图标:
setSmallIcon()必须调用,否则通知无法显示。 - 及时更新内容:比如下载进度、播放歌曲名,可使用
notificationManager.notify()更新同一NOTIFICATION_ID的通知。 - Android 13+ 需要通知权限:从 Android 13 开始,应用需要动态请求
POST_NOTIFICATIONS权限,否则通知无法显示,前台服务启动后系统会显示一个无内容的前台服务提示。务必在启动服务前处理权限请求。
// 在 Activity 中请求通知权限(Android 13+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
}
}
前台服务的生命周期管理
- 前台服务可通过
startService()启动,之后调用startForeground()提升为前台。 - 如果希望在任务结束后自动停止,可以在任务内调用
stopSelf()。 - 当服务被外部组件通过
stopService()停止时,系统会先调用服务的onDestroy(),你应在此释放资源。 - 如果服务是通过
bindService()绑定的,需同时解除绑定才能完全停止。
推荐做法:使用 WorkManager 或 JobScheduler 处理一次性任务,仅在真正需要长时间生存且用户已知晓的场景下使用前台服务。
总结与实践建议
-
何时使用前台服务?
当你需要执行一个长时间运行、用户希望保持活跃的任务(如音乐播放、导航、运动记录)且希望系统不轻易杀死该任务时。 -
关键注意点:
- 永远在
startForeground()的 5 秒内调用,否则系统会强制停止服务(Android 8.0+) - 正确声明
android:foregroundServiceType并请求必要的权限 - Android 13+ 必须请求通知权限
- 前台服务内不应执行耗时阻塞操作,可配合线程或协程
- 永远在
-
测试方法:
- 模拟系统回收:在开发者选项中启用“不保留活动”和“后台进程限制”
- 检查
adb shell dumpsys activity services中前台服务的运行状态
掌握前台服务是构建专业 Android 应用的重要一步,合理使用它能让后台任务既稳定又透明,赢得用户信任。