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_STICKY或START_REDELIVER_INTENT,则系统可能会尝试重新启动服务,并调用onStartCommand()方法(但请注意,对于START_STICKY,传递给onStartCommand()的Intent可能为null)。因此,在设计服务时,需要考虑到这一点,并确保服务能够正确处理这种情况。
相同点
- 目的:两者都用于启动Android中的Service,使得应用可以在后台执行长时间运行的操作,如音乐播放、文件下载、数据同步等。
- 生命周期:无论是通过
startService还是bindService启动的Service,都会经历创建(onCreate)、启动(onStartCommand/onBind)、运行等阶段,并最终在适当的时候被销毁(onDestroy)。
不同点
-
生命周期与组件关联
- startService:启动的Service与启动它的组件(如Activity)无关。即使启动它的Activity被销毁,Service仍然会继续运行,直到显式调用
stopService()或Service内部调用stopSelf()。 - bindService:创建的Service与调用它的组件(如Activity)绑定。当与之绑定的所有组件都被销毁并且解绑(unbindService())后,Service也会被销毁。
- startService:启动的Service与启动它的组件(如Activity)无关。即使启动它的Activity被销毁,Service仍然会继续运行,直到显式调用
-
交互方式
- startService:通常不提供与启动它的组件进行交互的直接方式。如果需要,可以通过广播(Broadcast)、Intent等方式间接通信。
- bindService:通过返回一个
IBinder接口,允许调用者与服务进行交互,如调用服务中的方法、获取服务中的数据等(Binder机制应用,非AIDL实现)。
-
启动与结束
- startService:多次调用
startService()并不会创建多个Service实例,而是多次调用了服务的onStartCommand()方法。服务需要通过stopService()或stopSelf()来显式结束。 - bindService:可以多次绑定到同一个Service,但Service只有一个实例。当所有客户端都解除绑定后,Service会自动停止。
- startService:多次调用
-
使用场景
- startService:更适用于那些不需要与用户界面进行交互、但需要运行较长时间的任务,如后台音乐播放、文件下载、数据同步等。
- bindService:适用于那些需要与用户界面交互或者与其他应用组件交互的服务,如即时聊天应用中的消息推送、游戏中的后台逻辑处理等。
-
优先级
- startService:通过
startService启动的服务可能会提高应用的优先级,以确保服务能够持续运行。 - bindService:与应用的优先级关系不大,主要取决于绑定它的组件的生命周期。
- startService:通过
先后调用 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()启动它的实例在运行。
理解这些交互对于管理后台服务和优化应用性能至关重要。