首页 / Android 入门教程 / Service 基础

Android 入门教程

Service 基础

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

AndroidAndroid 入门教程Service服务启动式服务绑定式服务生命周期

32. Service 基础

本节目标:理解 Service 是什么、两种服务(启动式与绑定式)的区别、Service 生命周期、为什么 Service 默认跑主线程,以及现代项目该不该用 Service。

Service 是什么

服务(Service)是没有界面的组件,用于在后台执行长时间运行的操作。即使用户切到别的应用,Service 也可能继续运行。

典型场景:

  • 后台播放音乐。
  • 后台下载/上传文件。
  • 跟服务器保持长连接。
  • 执行定时同步。
Warning
  • Service 默认跑在主线程。它不会自己开线程。在 Service 里做耗时操作(网络、数据库、文件 IO)一样会 ANR。必须在 Service 内部开线程(协程、线程池)。

Service vs Thread

很多人搞不清什么时候用 Service 什么时候用 Thread:

ServiceThread / 协程
生命周期独立于 Activity,Activity 销毁还能跑跟创建它的组件绑定,Activity 销毁一般也停
用户离开应用可继续运行(受后台限制)一般停止
是否有 Context是,能启动组件、访问资源不是
何时用需要后台长期运行、跨组件单纯把耗时操作挪出主线程

简单说:只想在 Activity 内部异步做事,用协程就够了。需要用户离开应用后还继续运行,才考虑 Service。

现代 Android 大量后台场景用 WorkManager 替代 Service(第 75 章讲),但音乐播放、定位导航等需要持续可见的场景仍用前台 Service。

两种 Service

1. 启动式服务(Started Service)

其他组件通过 startService() 启动,Service 在后台独立运行,跟启动者无关。启动者销毁了 Service 还在跑。Service 自己干完活调 stopSelf() 退出,或别人调 stopService() 停它。

适用:一次性后台任务,如下载文件、上传日志。

2. 绑定式服务(Bound Service)

其他组件通过 bindService() 绑定,建立客户端-服务端关系。绑定者能调用 Service 的方法(通过 IBinder)。所有绑定者都解绑后,Service 销毁。

适用:需要跟 Service 交互的场景,如音乐播放器 UI 控制 Service 播放/暂停。

一个 Service 能同时是启动式和绑定式,实现 onStartCommandonBind 两个回调即可。

声明 Service

在 Manifest 注册:

<application ...>
    <service
        android:name=".DownloadService"
        android:exported="false" />
</application>
  • android:name:Service 类名,必须。
  • android:exported:是否能被其他应用启动。false 表示只限本应用。
  • android:foregroundServiceType:前台服务类型(API 34+ 强制)。
Note
  • 启动 Service 必须用显式 Intent。隐式 Intent 启动 Service 从 Android 5.0 起抛异常(安全考虑)。

启动式 Service 示例

class DownloadService : Service() {

    override fun onCreate() {
        super.onCreate()
        // Service 创建时调用(只一次)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val url = intent?.getStringExtra("url") ?: return START_NOT_STICKY

        // 在协程里执行耗时操作
        serviceScope.launch(Dispatchers.IO) {
            try {
                downloadFile(url)
                // 完成后停止自己
                stopSelf(startId)
            } catch (e: Exception) {
                Log.e("DownloadService", "下载失败", e)
                stopSelf(startId)
            }
        }

        return START_NOT_STICKY
    }

    override fun onDestroy() {
        super.onDestroy()
        serviceScope.cancel()
    }

    override fun onBind(intent: Intent?): IBinder? = null    // 启动式返回 null

    private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
}

启动:

val intent = Intent(context, DownloadService::class.java).apply {
    putExtra("url", "https://example.com/file.zip")
}
ContextCompat.startForegroundService(context, intent)   // Android 8+ 用这个
// 或 context.startService(intent)(API 26+ 后台启动受限)

onStartCommand 返回值

返回值决定 Service 被系统杀后怎么重启:

返回值含义
START_STICKY重启 Service,但 intent 为 null。适合不依赖 intent 的长期服务(如音乐)
START_NOT_STICKY不重启。适合一次性任务(如下载)
START_REDELIVER_INTENT重启并重传最后一个 intent。适合必须完成的任务

绑定式 Service 示例

class MusicService : Service() {
    private val binder = LocalBinder()
    private var mediaPlayer: MediaPlayer? = null

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

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

    override fun onBind(intent: Intent?): IBinder = binder

    fun play(path: String) {
        mediaPlayer?.apply {
            reset()
            setDataSource(path)
            prepare()
            start()
        }
    }

    fun pause() = mediaPlayer?.pause()
    fun stop() = mediaPlayer?.stop()

    override fun onDestroy() {
        mediaPlayer?.release()
        mediaPlayer = null
        super.onDestroy()
    }
}

绑定:

class MusicActivity : ComponentActivity() {
    private var musicService: MusicService? = null
    private var bound = false

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val binder = service as MusicService.LocalBinder
            musicService = binder.getService()
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            musicService = null
            bound = false
        }
    }

    override fun onStart() {
        super.onStart()
        Intent(this, MusicService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        if (bound) {
            unbindService(connection)
            bound = false
        }
    }

    fun onPlayClick() = musicService?.play("/sdcard/song.mp3")
}

Service 生命周期

启动式:
  onCreate -> onStartCommand -> [运行] -> onDestroy

绑定式:
  onCreate -> onBind -> [绑定中] -> onUnbind -> onDestroy

混合:
  onCreate -> onStartCommand -> onBind -> [运行] -> onUnbind -> onDestroy
  • onCreate:创建时调一次,做初始化。
  • onStartCommand:每次 startService 都调(启动式)。
  • onBindbindService 时调,返回 IBinder(绑定式)。
  • onUnbind:所有客户端解绑时调。
  • onDestroy:Service 销毁时调,释放资源。

后台限制(重要)

Android 8.0(API 26)起对后台 Service 严格限制:

  • 应用在后台时,不能启动后台 ServicestartService 会抛 IllegalStateException
  • 需要后台执行要用 startForegroundService(),并在 5 秒内startForeground() 显示通知,变成前台服务。
  • 否则系统会认为违规,杀掉应用。

Android 14(API 34)进一步要求前台服务声明类型foregroundServiceType),且类型要跟实际用途匹配,否则崩溃。第 33 章细讲。

现代替代方案

很多传统 Service 场景现在有更好的方案:

传统做法现代推荐
后台上传/下载WorkManager(第 75 章)
定时同步WorkManager 周期任务
后台播放音乐前台 Service + MediaSession(仍用 Service)
后台定位前台 Service(location 类型)
Activity 内异步协程(不用 Service)
Tip
  • 新项目别滥用 Service。能用 WorkManager 解决的后台任务就用 WorkManager。只有需要持续可见的后台操作(音乐、导航、录屏)才用前台 Service。IntentService 已废弃,别用。

已废弃的类

  • IntentService:Android 11 起废弃。它用工作线程依次处理 Intent,但后台限制下无法正常运行。替代:WorkManager 或自己用 Service + 协程。
  • AsyncTask:API 30 起废弃。替代:协程。

小结

Service 是无界面后台组件,默认跑主线程,耗时操作要自己开线程。分启动式(startService,独立运行)和绑定式(bindService,可交互)。生命周期有 onCreate/onStartCommand/onBind/onUnbind/onDestroy。Android 8+ 后台启动 Service 受限,要用前台服务。现代项目大量场景用 WorkManager 替代 Service,只有持续可见的后台操作才用前台 Service。下一节深入前台服务。