Service启动和生命周期

476 阅读6分钟

Service在Android中有两种主要的启动模式,每种启动模式都有其特定的生命周期和行为:

  • 通过startService()启动的Started Service(启动服务);
  • 通过bindService()启动的Bound Service(绑定服务)。

Started Service(启动服务)的生命周期

  • onCreate():当服务首次被创建时调用,执行一次性初始化操作。 如果服务已经存在,则再次调用startService()不会触发此方法。
  • onStartCommand(Intent intent, int flags, int startId):当服务通过startService()方法被启动时调用。 如果服务已经在运行,则每次调用startService()都会调用此方法,并可能传递新的Intent和启动ID。 此方法中可以执行具体的任务。
  • onDestroy():当服务不再需要且将被销毁时调用。服务应在此方法中释放占用的资源,如关闭文件、停止线程等。

Bound Service(绑定服务)的生命周期

  • onCreate():与服务被创建时相同,执行初始化工作。
  • onBind(Intent intent):当客户端(如Activity)通过bindService()方法与服务绑定时调用。 此方法必须返回一个IBinder对象,以便客户端能够与服务进行通信。
  • onUnbind(Intent intent):当所有客户端都与服务解绑时调用。 如果此方法返回true,则服务在后续可能会被重新绑定并调用onRebind(); 如果返回false,则服务将完全销毁。
  • onRebind(Intent intent):当服务已经解绑,但随后又被重新绑定时调用。
  • onDestroy():与服务被销毁时相同,释放资源。

StartCommand方法的参数

onStartCommand(Intent intent, int flags, int startId)方法有三个参数:

  • Intent intent:传递给startService()的Intent对象,包含了启动服务时所需的数据。
  • int flags:提供了关于命令的额外信息。常见的值有0(正常启动)、 START_FLAG_REDELIVERY(服务被杀死后重启,且之前onStartCommand()返回START_REDELIVER_INTENT)、 START_FLAG_RETRY(如果在启动服务时onStartCommand()方法没有被调用,会再次尝试调用)。
  • int startId:一个唯一的标识符,用于标识这次启动请求。通过它可以区分多次启动请求。

能否执行耗时操作

是的,onStartCommand()方法中可以执行耗时操作,但需要注意以下几点:

  • 耗时操作不应该在主线程(UI线程)中执行,以避免阻塞用户界面和导致应用无响应(ANR)。应该使用新的线程或线程池来执行耗时操作。
  • 在耗时操作完成后,如果需要停止服务,可以调用stopSelf()方法(如果需要基于某个特定启动请求ID来停止服务,则可以使用stopSelfResult(int startId))。
  • 如果服务被系统杀死,并且onStartCommand()的返回值是START_STICKYSTART_REDELIVER_INTENT,则系统可能会尝试重新启动服务,并调用onStartCommand()方法(但请注意,对于START_STICKY,传递给onStartCommand()的Intent可能为null)。因此,在设计服务时,需要考虑到这一点,并确保服务能够正确处理这种情况。

相同点

  • 目的:两者都用于启动Android中的Service,使得应用可以在后台执行长时间运行的操作,如音乐播放、文件下载、数据同步等。
  • 生命周期:无论是通过startService还是bindService启动的Service,都会经历创建(onCreate)、启动(onStartCommand/onBind)、运行等阶段,并最终在适当的时候被销毁(onDestroy)。

不同点

  1. 生命周期与组件关联

    • startService:启动的Service与启动它的组件(如Activity)无关。即使启动它的Activity被销毁,Service仍然会继续运行,直到显式调用stopService()或Service内部调用stopSelf()
    • bindService:创建的Service与调用它的组件(如Activity)绑定。当与之绑定的所有组件都被销毁并且解绑(unbindService())后,Service也会被销毁。
  2. 交互方式

    • startService:通常不提供与启动它的组件进行交互的直接方式。如果需要,可以通过广播(Broadcast)、Intent等方式间接通信。
    • bindService:通过返回一个IBinder接口,允许调用者与服务进行交互,如调用服务中的方法、获取服务中的数据等(Binder机制应用,非AIDL实现)。
  3. 启动与结束

    • startService:多次调用startService()并不会创建多个Service实例,而是多次调用了服务的onStartCommand()方法。服务需要通过stopService()stopSelf()来显式结束。
    • bindService:可以多次绑定到同一个Service,但Service只有一个实例。当所有客户端都解除绑定后,Service会自动停止。
  4. 使用场景

    • startService:更适用于那些不需要与用户界面进行交互、但需要运行较长时间的任务,如后台音乐播放、文件下载、数据同步等。
    • bindService:适用于那些需要与用户界面交互或者与其他应用组件交互的服务,如即时聊天应用中的消息推送、游戏中的后台逻辑处理等。
  5. 优先级

    • startService:通过startService启动的服务可能会提高应用的优先级,以确保服务能够持续运行。
    • bindService:与应用的优先级关系不大,主要取决于绑定它的组件的生命周期。

先后调用 startService()bindService()如何关闭服务

在Android中,Service 的生命周期和行为依赖于它是如何被启动和绑定的。

1. 调用 startService() 后调用 bindService()

当你首先调用 startService() 启动一个服务时,服务会独立于启动它的组件(如Activity)运行。即使启动它的组件被销毁了,服务仍然继续运行。此时,如果你调用 bindService() 来绑定到这个服务,服务的生命周期将同时受到启动和绑定的影响。

  • 调用 unbindService() 会停止服务吗?

    不会。调用 unbindService() 只会解除客户端(如Activity)与服务之间的绑定,但不会停止服务。如果服务之前是通过 startService() 启动的,它将继续运行,直到被 stopService() 或自身调用 stopSelf() 停止。

2. 先调用 bindService() 再调用 startService()

如果首先通过 bindService() 绑定到一个服务,并且随后通过 startService() 启动该服务,服务的生命周期将同时由这两个操作控制。

  • 调用 stopService() 会停止服务吗?

    是的,但是情况有点复杂。 如果服务只被绑定(即只调用了 bindService()),那么调用 stopService() 不会有任何效果,因为服务不是由 startService() 启动的。 但是,如果服务既被绑定又被启动(即先调用 bindService() 后调用 startService()),那么调用 stopService() 会请求系统停止服务。 然而,如果此时仍有客户端绑定到服务,服务将不会真正停止,直到所有绑定都被解除(通过 unbindService())。 一旦所有绑定都被解除,并且 stopService() 被调用,服务才会停止。

总结

  • unbindService():仅解除绑定,不影响由 startService() 启动的服务。
  • stopService():请求停止服务,但如果有客户端绑定到服务,则服务将保持运行状态,直到所有绑定都被解除。

服务将一直运行,直到:

  • 它自己调用 stopSelf()
  • 所有启动它的 startService() 调用都被相应的 stopService() 调用所平衡。
  • 所有绑定到它的客户端都调用了 unbindService(),并且没有任何通过 startService() 启动它的实例在运行。

理解这些交互对于管理后台服务和优化应用性能至关重要。