Foreground Service 生命周期:四个关键方法

今天我们来聊聊 Foreground Service 的生命周期。

说白了,就是系统会在什么时候调用你写的那些回调方法。

我刚开始学 Android 时,总以为 Service 和 Activity 差不多。

后来发现,差远了。尤其是 Foreground Service,它有自己的脾气。

一个典型的 Foreground Service,它的生命周期主要围绕四个方法:

  • onCreate()
  • onStartCommand()
  • onBind()
  • onDestroy()

这四个方法,就是 Service 的“生老病死”。

你想想看,搞懂了它们的调用时机和职责,写出来的 Service 才稳。

onCreate:Service 的“出生证”

onCreate() 在 Service 第一次被创建时调用。

注意,是“第一次”。如果 Service 已经存在,再次启动它就不会再调用了。

这个方法最适合做什么?

  • 初始化全局变量
  • 创建通知渠道
  • 准备线程池或数据库连接

我个人习惯把通知的 NotificationChannel 创建逻辑放在这里。

因为 onCreate 在整个 Service 生命周期里只执行一次,非常适合做这种“一次性”的初始化工作。

技巧:如果初始化失败,可以在 onCreate 里直接调用 stopSelf(),阻止 Service 继续运行。

典型代码结构:

@Override
public void onCreate() {
    super.onCreate();
    // 创建通知渠道
    createNotificationChannel();
    // 初始化线程池
    executorService = Executors.newFixedThreadPool(4);
    // 记录日志
    Log.d(TAG, "Service 已创建");
}

onStartCommand:任务的“发令枪”

这才是 Foreground Service 的核心战场。

每次调用 startForegroundService() 或 startService(),onStartCommand 都会被触发。

这意味着,它可以被多次调用。嗯,这点很重要。

来看方法签名:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 你的任务逻辑
    return START_STICKY;
}

三个参数里,intent 携带启动指令,flags 表示重启标志,startId 是本次启动的唯一标识。

返回值?这里有三种选择,我当年踩过不少坑:

返回值 行为 适用场景
START_STICKY Service 被杀死后,系统会尝试重建,并传入 null Intent 调用 onStartCommand 需要持续运行,但不依赖启动参数的后台任务
START_NOT_STICKY Service 被杀死后,系统不会自动重启,直到有新的显式启动请求 任务完成后不希望被系统无故重启的场景
START_REDELIVER_INTENT Service 被杀死后,系统会重建并重新传递最后一次的 Intent 必须处理上一个任务的场景,比如下载文件
警告:我曾经遇到过一个诡异的问题——Service 的 onStartCommand 里做了个网络请求,结果系统把 Service 杀了,通知也掉了,用户以为 App 崩了。
后来改成 START_REDELIVER_INTENT 并配合 Foreground Service 的通知重建逻辑,问题才解决。
所以,选对返回值真的很关键。

onBind:绑定的“桥梁”

这个方法在绑定 Service 时调用,也就是其他组件通过 bindService() 连接时。

对于 Foreground Service 来说,onBind 用的其实不多。因为大部分时长任务都不需要频繁交互。

但如果你是做音乐播放器、导航这类需要双向通信的场景,onBind 就很重要了。

它返回一个 IBinder 对象,客户端可以通过这个对象调用 Service 里的方法。

@Override
public IBinder onBind(Intent intent) {
    // 返回 Binder 实例
    return new LocalBinder();
}

我一般只在需要实时反馈进度的时候才用绑定。

比如大文件上传,客户端需要知道上传百分比。

这时候绑定 Service 比用广播靠谱得多。

技巧:如果 Foreground Service 不需要绑定,直接返回 null 就行。系统不会强制你实现这个方法。

onDestroy:体面的“谢幕”

Service 结束时的清理工作,全看这个方法了。

系统会在以下情况调用 onDestroy:

  • 调用 stopSelf() 或 stopService()
  • 系统需要回收内存,强制杀死 Service

注意:系统强杀时,onDestroy 可能不会被执行。这点要有心理准备。

@Override
public void onDestroy() {
    super.onDestroy();
    // 关闭线程池
    if (executorService != null) {
        executorService.shutdown();
    }
    // 取消通知
    stopForeground(STOP_FOREGROUND_REMOVE);
    // 释放资源
    Log.d(TAG, "Service 已销毁");
}

我曾经在 onDestroy 里忘记调用 stopForeground,结果 Service 死了,通知栏还挂着。

用户点通知,APP 直接崩。嗯,这种错误太低级了,大家引以为戒。

生命周期流程图

下面这张图,把整个生命周期串起来了。我建议你多看几遍,理解每个方法之间的流转关系。

Foreground Service 生命周期流程 startForegroundService() 或 startService() 调用 onCreate() 仅第一次创建时调用 onStartCommand() 每次启动都会调用 onBind() 绑定模式可选 执行 Foreground 长时间任务 线程池 / 协程 / WorkManager onDestroy() 清理资源,移除通知 注意:系统强杀 Service 时,onDestroy 不一定执行,但通知会被移除 推荐在 onStartCommand 中调用 startForeground() 确保通知可见
总结一下:
  • onCreate 做一次性的初始化
  • onStartCommand 处理每次启动的任务逻辑
  • onBind 按需实现,绑定通信
  • onDestroy 做好资源清理工作

牢记每个方法的调用时机,你的 Foreground Service 才能稳定运行。