Service相关知识
一. 简介
Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。Service可由其他应用组件启动,而且即使用户切换到其他应用,Service仍将在后台继续运行。 此外,组件可以绑定到Service,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,Service可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
二. 创建与使用
要创建服务,您必须创建 Service 的子类(或使用它的一个现有子类)。在实现中,您需要重写一些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务(如适用)。 应重写的最重要的回调方法包括:
-
onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动Service时,系统将调用此方法。一旦执行此方法,Service即会启动并可在后台无限期运行。 如果实现了此方法,则在Service工作完成后,需要由通过调用 stopSelf() 或 stopService() 来停止Service。(如果只想提供绑定,则无需实现此方法。) -
onBind()
当另一个组件想通过调用 bindService() 与Service绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须通过返回 IBinder 提供一个接口,供客户端用来与Service进行通信。请务必实现此方法,但如果我们并不希望允许绑定,则应返回 null。 -
onCreate()
首次创建Service时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果Service已在运行,则不会调用此方法。 -
onDestroy()
当Service不再使用且将被销毁时,系统将调用此方法。Service应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是Service接收的最后一个调用。
1. AndroidManifest文件中声明Service
Service是Android的组件,所以需要在manifest文件声明,添加<service>元素作为<application>的子元素。在 <service> 元素中,可以定义一些特性,如启动Service及其运行所在进程所需的权限。android:name 属性是唯一必需的属性,用于指定Service的类名。应用一旦发布,即不应更改此类名,如若不然,可能会存在因依赖显式 Intent 启动或绑定Service而破坏代码的风险。
为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为Service声明 Intent 过滤器。 启动哪个Service存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为Service提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标Service的不确定性。
此外,还可以通过添加 android:exported 属性并将其设置为 "false",确保Service仅适用于当前的应用。这可以有效阻止其他应用启动我们的Service,即便在使用显式 Intent 时也如此。
2. 启动Service
在组件中启动Service有两种方式,一种是通过startService,另一种是bindService。
(1)startService
可以通过将 Intent(指定要启动的Service)传递给 startService(),从 Activity 或其他应用组件启动Service。Android 系统调用Service的 onStartCommand() 方法,并向其传递 Intent。(切勿直接调用 onStartCommand()。)
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService() 方法将立即返回,且 Android 系统调用Service的 onStartCommand() 方法。如果Service尚未运行,则系统会先调用 onCreate(),然后再调用 onStartCommand()。
如果Service亦未提供绑定,则使用 startService() 传递的 Intent 是应用组件与Service之间唯一的通信模式。但是,如果希望Service返回结果,则启动服务的客户端可以为广播创建一个 PendingIntent (使用 getBroadcast()),并通过启动Service的 Intent 传递给Service。然后,Service就可以使用广播传递结果。
多个Service启动请求会导致多次对Service的 onStartCommand() 进行相应的调用。但是,要停止Service,只需一个Service停止请求(使用 stopSelf() 或 stopService())即可。
(2)bindService
绑定Service允许应用组件通过调用 bindService() 与其绑定,以便创建长期连接(通常不允许组件通过调用 startService() 来启动它)。
如需与 Activity 和其他应用组件中的Service进行交互,或者需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定Service。
要创建绑定Service,必须实现 onBind() 回调方法以返回 IBinder,用于定义与Service通信的接口。然后,其他应用组件可以调用 bindService() 来检索该接口,并开始对Service调用方法。Service只用于与其绑定的应用组件,因此如果没有组件绑定到Service,则系统会销毁Service(不必像通过onStartCommand() 启动的Service那样来停止绑定Service)。
要创建绑定Service,首先必须定义指定客户端如何与Service通信的接口。 Service与客户端之间的这个接口必须是 IBinder 的实现,并且Service必须从 onBind()回调方法返回它。一旦客户端收到 IBinder,即可开始通过该接口与Service进行交互。
多个客户端可以同时绑定到Service。客户端完成与Service的交互后,会调用 unbindService() 取消绑定。一旦没有客户端绑定到该服务,系统就会销毁它。
3. Service生命周期
Service的生命周期要比Activity简单,但是也要密切关注如何创建和销毁Service,因为Service可以在用户没有意识到的情况下运行于后台。Service的生命周期由于启动方式的不同也有不同,下图展示了两种情况的生命周期
image.png
-
Service的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,Service也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
无论Service是通过 startService() 还是 bindService() 创建,都会为所有Service调用 onCreate() 和 onDestroy() 方法。
-
Service的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
对于startService,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于bindService,有效生命周期在 onUnbind() 返回时结束。
注:尽管startService是通过调用 stopSelf() 或 stopService() 来停止,但是该Service并无相应的回调(没有 onStop() 回调)。因此,除非Service绑定到客户端,否则在Service停止时,系统会将其销毁 ,onDestroy() 是接收到的唯一回调。
在实际使用过程中要根据需要实现生命周期的回调方法
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
关于onStartCommand方法的返回值,必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:
-
START_NOT_STICKY
如果系统在 onStartCommand() 返回后终止Service,则除非有挂起 Intent 要传递,否则系统不会重建Service。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行Service。
-
START_STICKY
如果系统在 onStartCommand() 返回后终止Service,则会重建Service并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动Service(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似Service)。
-
START_REDELIVER_INTENT
如果系统在 onStartCommand() 返回后终止Service,则会重建Service,并通过传递给Service的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的Service。