Service的详细使用

0 阅读5分钟

一、Service 两大类型

1. Started Service(启动服务)

startService(intent) 开启,一旦启动独立长期运行,与页面解绑,主动调用 stopSelf() / stopService() 才销毁。适用:后台上传、定时同步、日志上报、离线缓存。

2. Bound Service(绑定服务)

bindService(intent, connection, flags) 绑定,和调用方生命周期绑定,所有客户端全部 unbind 后服务销毁。适用:跨进程通信、频繁交互(如播放器控制、DataCenter 数据代理),对应你之前 ContentProvider.call 同类 IPC 场景。

3. 混合模式(start + bind 同时使用)

同时调用 startService + bindService:必须同时执行 stopService + 全部 unbind,服务才会销毁。

4. 前台服务 Foreground Service

Started Service 改造,弹出系统通知,不会被系统轻易杀死,如音乐播放、导航。

5. IntentService(废弃,API30+ 推荐 WorkManager / Coroutine)

内部单一线程自动处理任务,执行完自动销毁;已标记过时,不推荐新项目使用。


二、Service 全部生命周期方法(按执行顺序)

完整列表(生命周期回调,全部重写入口)

Service 原生全部回调方法一共 6 个

  1. onCreate()
  2. onStartCommand()
  3. onBind()
  4. onUnbind()
  5. onRebind()
  6. onDestroy()

逐个说明触发条件

  1. onCreate服务第一次实例化执行一次,不管 start /bind 只走一次。
  2. onStartCommandstartService() 触发,每次启动都会调用。
  3. onBind仅第一次 bindService() 触发,返回 IBinder。
  4. onUnbind所有客户端全部解绑时触发。
  5. onRebind解绑后再次绑定才会触发(前提 onUnbind 返回 true)。
  6. onDestroy服务最终销毁唯一回调,资源释放。

补充区分:不属于生命周期回调的方法

  • startForeground():普通业务 API,不是系统生命周期回调
  • stopSelf() / stopSelf(startId):开发者主动调用停止,不是回调
  • AIDL 自定义接口方法:自己写的,不属于 Service 原生回调

三种场景实际执行到的回调数量

  1. 只 startService:用到 3 个 → onCreate、onStartCommand、onDestroy
  2. 只 bindService:用到 4 个 → onCreate、onBind、onUnbind、onDestroy
  3. start + bind 混合反复解绑重绑:6 个生命周期回调全部走完

通用全部回调(基础 Service)

1. onCreate()

  • 调用时机:服务第一次创建时执行 1 次,多次 start/bind 不会重复调用
  • 作用:初始化数据库、线程池、管理器、全局资源

kotlin

override fun onCreate() {
    super.onCreate()
    // 只执行一次初始化
}

2. onStartCommand(intent: Intent?, flags: Int, startId: Int)

Started Service 触发,每次 startService 都会调用返回值决定系统杀后台后的重启策略:

  1. START_STICKY:被杀后自动重启,intent 为 null(音乐播放器)
  2. START_NOT_STICKY:被杀不重启(一次性任务)
  3. START_REDELIVER_INTENT:被杀重启并重新传入原 Intent(断点续传)
  4. START_STICKY_COMPATIBILITY 兼容低版本

kotlin

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    // 处理后台任务,建议协程/线程执行耗时操作
    return START_NOT_STICKY
}

3. onBind(intent: Intent): IBinder?

Bound Service 触发,第一次 bindService 调用返回 IBinder 对象,作为客户端跨进程通信的媒介

kotlin

// 内部Binder,同进程通信
inner class LocalBinder : Binder() {
    fun getService(): DataCenterService = this@DataCenterService
}
private val binder = LocalBinder()

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

4. onRebind(intent: Intent?)

客户端全部解绑后,再次绑定服务时触发(仅混合模式生效)

5. onUnbind(intent: Intent?): Boolean

所有客户端全部 unbind 时调用返回 true → 下次绑定触发 onRebind;false 不会触发

6. onDestroy()

服务销毁前最后回调,释放资源、关闭线程、注销监听、停止通知无论启动 / 绑定模式,销毁必走此方法

kotlin

override fun onDestroy() {
    super.onDestroy()
    // 释放资源,防止内存泄漏
}

补充:前台服务专属方法

startForeground(notificationId, Notification)onStartCommand 调用,提升服务优先级,避免被系统回收;API26+ 必须配通知渠道。


三、两种服务完整生命周期流程

流程 1:单纯 StartService(启动服务)

  1. onCreate() → 2. onStartCommand()停止方式:调用 stopSelf(startId) / stopService(intent)销毁:onDestroy()

多次 startService:只走 onStartCommand,不会重复 onCreate

流程 2:单纯 BindService(绑定服务)

  1. onCreate() → 2. onBind()全部客户端解绑:onUnbind()onDestroy()

流程 3:混合 start + bind

  1. onCreate → onStartCommand → onBind解绑所有客户端:仅执行 onUnbind,服务不会销毁(因为 start 标记存在)必须手动 stopService 才会走到 onDestroy

四、完整代码示例

1. Started Service 示例(后台任务)

kotlin

class BackSyncService : Service() {
    override fun onCreate() {
        super.onCreate()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 协程执行耗时同步
        CoroutineScope(Dispatchers.IO).launch {
            syncDataCenter()
            // 任务完成自动停止服务
            stopSelf(startId)
        }
        return START_NOT_STICKY
    }

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

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

    private fun syncDataCenter() {
        // 同步接口逻辑
    }
}

启动:

kotlin

val intent = Intent(context, BackSyncService::class.java)
context.startService(intent)
// 停止
context.stopService(intent)

2. Bound Service 示例(同进程交互)

kotlin

class DataCenterBindService : Service() {
    inner class LocalBinder : Binder() {
        fun getService(): DataCenterBindService = this@DataCenterBindService
    }
    private val binder = LocalBinder()

    // 对外提供方法
    fun queryResult(): DataCenterResult {
        return DataCenterResult(0, "success", null)
    }

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

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

    override fun onUnbind(intent: Intent?): Boolean {
        return true // 支持rebind
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
    }

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

客户端绑定调用:

kotlin

private var service: DataCenterBindService? = null
private val conn = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, ibinder: IBinder?) {
        val binder = ibinder as DataCenterBindService.LocalBinder
        service = binder.getService()
        // 调用服务方法
        val res = service?.queryResult()
    }
    override fun onServiceDisconnected(name: ComponentName?) {
        service = null
    }
}

// 绑定
val intent = Intent(this, DataCenterBindService::class.java)
bindService(intent, conn, Context.BIND_AUTO_CREATE)

// 解绑(页面销毁必须调用,否则内存泄漏)
unbindService(conn)

3. 前台服务片段

kotlin

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val channelId = "service_channel"
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(channelId, "后台同步", NotificationManager.IMPORTANCE_LOW)
        val nm = getSystemService(NotificationManager::class.java)
        nm.createNotificationChannel(channel)
    }
    val notification = NotificationCompat.Builder(this, channelId)
        .setContentTitle("数据同步中")
        .setSmallIcon(R.mipmap.ic_launcher)
        .build()
    startForeground(1001, notification)
    return START_STICKY
}

五、四大方法核心区别对比 Service vs ContentProvider

  1. Service:适合持续后台任务、双向方法调用、长连接;分启动 / 绑定两种模式,生命周期灵活
  2. ContentProvider:标准结构化数据共享,主打 CRUD 表格;call 仅作为补充扩展接口,不适合持续后台运行

六、关键注意点

  1. Service 默认运行在主线程,耗时操作必须手动开子线程 / 协程;

  2. bindService 必须配对 unbind,Activity/Fragment 销毁不解绑会内存泄漏;

  3. Android8.0 禁止后台静默启动 Service,后台任务优先使用 WorkManager;

  4. 跨进程通信推荐两种方案:

    • 频繁方法调用:Bound Service + AIDL
    • 简单单次请求:ContentProvider.call
  5. 不要在 Service 做无限循环耗时任务,合理控制生命周期及时 stopSelf。