首页 / Android 入门教程 / 前台服务

Android 入门教程

前台服务

本教程共 100 篇 · 第 33 篇 · 更新于 2026-07-28 · 约 9 分钟阅读

AndroidAndroid 入门教程前台服务startForeground通知foregroundServiceType后台限制

33. 前台服务

本节目标:理解前台服务是什么、为什么需要它、怎么用 startForeground 绑定通知、前台服务类型怎么选、Android 14+ 的新要求和后台限制。

什么是前台服务

前台服务(Foreground Service)是一种特殊的服务,它显示一个持续通知告诉用户「我正在后台干活」。因为有这个可见通知,系统把它当高优先级,不会轻易杀掉。

典型场景:

  • 音乐播放器后台播放。
  • 导航应用后台持续定位。
  • 录屏、录音。
  • 文件下载(大文件、长时间)。
  • 运动追踪。
Note
  • 前台服务和普通服务的区别:前台服务有持续通知,系统认为它对用户可见、重要,优先级高(接近前台进程),不会被内存回收杀掉。普通后台服务随时可能被杀。

为什么需要前台服务

Android 8.0(API 26)起,应用在后台时不能启动普通 Service(startService 抛异常)。但用户切到后台时确实需要继续干活(如听歌),怎么办?

答案是 startForegroundService() 启动一个前台服务,并在 5 秒内调 startForeground() 显示通知。这样系统允许它继续运行。

Warning
  • 5 秒内必须调 startForeground(),否则系统抛 ForegroundServiceDidNotStartInTimeException,应用崩溃。这是新手常踩的坑。

基本用法

1. 声明权限和服务类型

Manifest:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<!-- Android 14+ 还要声明对应类型的权限 -->

<application ...>
    <service
        android:name=".MusicService"
        android:exported="false"
        android:foregroundServiceType="mediaPlayback" />
</application>
Warning
  • Android 14(API 34)起,foregroundServiceType强制的。每个前台服务必须声明类型,且要声明对应的 FOREGROUND_SERVICE_XXX 权限。不声明直接崩。

2. 创建通知渠道

Android 8.0+ 通知必须建 Channel:

class MusicService : Service() {
    companion object {
        const val CHANNEL_ID = "music_playback"
        const val NOTIFICATION_ID = 1
    }

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                CHANNEL_ID,
                "音乐播放",
                NotificationManager.IMPORTANCE_LOW      // 播放服务用低重要性,不响铃
            ).apply {
                description = "显示正在播放的音乐"
            }
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
}

3. 构建通知并 startForeground

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val notification = NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle("正在播放")
        .setContentText("歌曲名 - 歌手")
        .setSmallIcon(R.drawable.ic_music_note)        // 必须有
        .setOngoing(true)                                // 不可滑动清除
        .setContentIntent(pendingIntent)                 // 点击通知回到应用
        .build()

    // Android 14+ 要传类型
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
    } else {
        startForeground(NOTIFICATION_ID, notification)
    }

    // 开始实际工作
    startPlayback()

    return START_STICKY
}

4. 启动服务

val intent = Intent(context, MusicService::class.java)
ContextCompat.startForegroundService(context, intent)

startForegroundService 会启动服务并调用 onStartCommand,你在里面调 startForeground 转前台。

5. 停止前台服务

// Service 内部干完活
stopForeground(STOP_FOREGROUND_REMOVE)     // 移除通知
stopSelf()                                  // 停止服务

// 或外部停止
context.stopService(Intent(context, MusicService::class.java))

前台服务类型

Android 14 起强制声明类型,常见类型:

类型用途需要的额外权限
mediaPlayback音频/视频播放FOREGROUND_SERVICE_MEDIA_PLAYBACK
mediaProjection录屏FOREGROUND_SERVICE_MEDIA_PROJECTION
camera相机(如视频通话)FOREGROUND_SERVICE_CAMERA + CAMERA
microphone录音FOREGROUND_SERVICE_MICROPHONE + RECORD_AUDIO
location持续定位(导航)FOREGROUND_SERVICE_LOCATION + 定位权限
dataSync数据同步、上传下载FOREGROUND_SERVICE_DATA_SYNC
health健康监测FOREGROUND_SERVICE_HEALTH
connectedDevice连接外设(蓝牙、USB)FOREGROUND_SERVICE_CONNECTED_DEVICE
remoteMessaging车机消息转发FOREGROUND_SERVICE_REMOTE_MESSAGING
systemExempted系统级豁免特定应用
Note
  • 类型要跟实际用途匹配。声明 mediaPlayback 但实际做下载,审核会拒,运行时也可能被系统杀。
  • 一个服务能声明多个类型(用 \| 连接),但通常一个就够。

后台启动限制

Android 12(API 31)起,应用在后台时不能启动前台服务,除非满足豁免条件:

  • 用户操作触发(如点击通知、点 widget)。
  • 高优先级 FCM 消息。
  • 系统广播(如 BOOT_COMPLETED)。
  • 前台应用启动。

不满足豁免时调用 startForegroundService 会抛 ForegroundServiceStartNotAllowedException

Tip
  • 应用在后台需要立即执行的短任务,用 expedited WorkManager(加急工作),它能短暂运行类似前台服务,不用自己管通知。第 75 章讲。

通知的必要性

前台服务的通知不能被用户滑动清除setOngoing(true)),除非:

  • 服务调 stopForeground / stopSelf
  • 用户在通知设置里关掉。
  • 绑定的 Activity 调用 stopService

通知内容要清晰告诉用户在干什么,否则用户会觉得「这通知怎么删不掉」而反感。

Warning
  • setSmallIcon 必须设,不设通知不显示,startForeground 会失败。
  • Android 13+ 还要 POST_NOTIFICATIONS 运行时权限,用户拒绝也能跑前台服务,但通知不显示(用户体验差)。

实战:音乐播放前台服务

class MusicService : Service() {
    private val binder = LocalBinder()
    private var mediaPlayer: MediaPlayer? = null
    private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

    inner class LocalBinder : Binder() {
        fun getService() = this@MusicService
    }

    override fun onCreate() {
        super.onCreate()
        createChannel()
        mediaPlayer = MediaPlayer()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(NOTIFICATION_ID, buildNotification("准备播放"))
        // 实际播放逻辑
        return START_STICKY
    }

    fun play(song: Song) {
        mediaPlayer?.apply {
            reset()
            setDataSource(song.path)
            prepare()
            start()
        }
        updateNotification("正在播放:${song.title}")
    }

    private fun updateNotification(text: String) {
        val manager = getSystemService(NotificationManager::class.java)
        manager.notify(NOTIFICATION_ID, buildNotification(text))
    }

    private fun buildNotification(text: String): Notification {
        val pendingIntent = PendingIntent.getActivity(
            this, 0,
            Intent(this, MainActivity::class.java),
            PendingIntent.FLAG_IMMUTABLE
        )
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("音乐播放器")
            .setContentText(text)
            .setSmallIcon(R.drawable.ic_music_note)
            .setOngoing(true)
            .setContentIntent(pendingIntent)
            .build()
    }

    override fun onBind(intent: Intent?) = binder

    override fun onDestroy() {
        mediaPlayer?.release()
        serviceScope.cancel()
        super.onDestroy()
    }
}

何时该用前台服务

场景用不用前台服务
后台播放音乐用(mediaPlayback)
导航持续定位用(location)
录屏、通话用(mediaProjection / camera / microphone)
大文件下载看情况,能用 WorkManager 就用 WorkManager
定时同步不用,用 WorkManager
一次性短任务不用,用协程或 WorkManager
Tip
  • 决策原则:需要持续可见、用户主动开启的后台操作用前台服务;可延迟、不需用户感知的用 WorkManager。

小结

前台服务显示持续通知,系统优先级高不被杀。用 startForegroundService 启动,5 秒内必须 startForeground 显示通知。Android 14+ 强制声明 foregroundServiceType 和对应权限。Android 12+ 后台启动受限。场景:音乐播放、导航、录屏通话。普通后台任务用 WorkManager。下一节学 Service 与进程。