四大组件之二 -- Service

字数 3020阅读 14

此章节参考Android官网内容而编写,如有遗漏,后面会慢慢补上。

一、Service是什么。

Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。
例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

服务分为两种形式:

启动:当应用组件(如 Activity)通过调用startService()启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。

绑定:当应用组件通过调用bindService()绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

您的服务可以同时以这两种方式运行,也就是说,它既可以是启动服务(以无限期运行),也允许绑定。问题只是在于您是否实现了一组回调方法:onStartCommand()(允许组件启动服务)和onBind()(允许绑定服务)。

二、创建Service。

要创建服务,您必须创建Service的子类(或使用它的一个现有子类)。在实现中,您需要重写一些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务(如适用)。 应重写的最重要的回调方法包括:

onStartCommand():当另一个组件(如 Activity)通过调用startService()请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用stopSelf()或stopService()来停止服务。(如果您只想提供绑定,则无需实现此方法。)

onBind():当另一个组件想通过调用bindService()与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回IBinder提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。

onCreate():首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用onStartCommand()或onBind()之前)。如果服务已在运行,则不会调用此方法。

onDestory():当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

如果组件通过调用startService()启动服务(这会导致对onStartCommand()的调用),则服务将一直运行,直到服务使用stopSelf()自行停止运行,或由其他组件通过调用stopService()停止它为止。

如果组件是通过调用bindService()来创建服务(且调用onStartcommand(),则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。

使用清单文件声明服务:

要声明服务,请添加<service>元素作为<application>元素的子元素。

<service      description="string resource"
android:directBootAware=["true"|"false"]
android:enabled=["true"|"false"]
android:exported=["true"|"false"]
android:icon="drawable resource"
android:isolatedProcess=["true"|"false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string"   >
</service>

android:name属性是唯一必需的属性,用于指定服务的类名。应用一旦发布,即不应更改此类名,如若不然,可能会存在因依赖显式 Intent 启动或绑定服务而破坏代码的风险.

为了确保应用的安全性,请始终使用显式 Intent 启动或绑定Service,且不要为服务声明 Intent 过滤器。

启动服务:

启动服务由另一个组件通过调用startService()启动,这会导致调用服务的onStartCommand()方法。

服务启动之后,其生命周期即独立于启动它的组件,并且可以在后台无限期地运行,即使启动服务的组件已被销毁也不受影响。 因此,服务应通过调用stopSelf()结束工作来自行停止运行,或者由另一个组件通过调用stopService()来停止它。

应用组件(如 Activity)可以通过调用startService()方法并传递Intent对象(指定服务并包含待使用服务的所有数据)来启动服务。服务通过onStartCommand()方法接收此Intent

例如,假设某 Activity 需要将一些数据保存到在线数据库中。该 Activity 可以启动一个协同服务,并通过向startService()传递一个 Intent,为该服务提供要保存的数据。服务通过onStartCommand()接收 Intent,连接到互联网并执行数据库事务。事务完成之后,服务会自行停止运行并随即被销毁。

您可以扩展两个类来创建启动服务:

Service 

这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。

IntentService

这是Service的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现onHandleIntent()方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。

例如,Activity 可以结合使用显式 Intent 与startService(),启动上文中的示例服务 (HelloService):

Intentintent=newIntent(this,HelloService.class);

startService(intent);

startService()方法将立即返回,且 Android 系统调用服务的onStartCommand()方法。如果服务尚未运行,则系统会先调用onCreate(),然后再调用onStartCommand()

如果服务亦未提供绑定,则使用startService()传递的 Intent 是应用组件与服务之间唯一的通信模式。但是,如果您希望服务返回结果,则启动服务的客户端可以为广播创建一个PendingIntent(使用getBroadcast()),并通过启动服务的Intent传递给服务。然后,服务就可以使用广播传递结果。

多个服务启动请求会导致多次对服务的onStartCommand()进行相应的调用。但是,要停止服务,只需一个服务停止请求(使用stopSelf()stopService())即可。

停止服务:

启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在onStartCommand()返回后会继续运行。因此,服务必须通过调用stopSelf()自行停止运行,或者由另一个组件通过调用stopService()来停止它。

一旦请求使用stopSelf()stopService()停止服务,系统就会尽快销毁服务。

但是,如果服务同时处理多个onStartCommand()请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用stopSelf(int)确保服务停止请求始终基于最近的启动请求。也就说,在调用stopSelf(int)时,传递与停止请求的 ID 对应的启动请求的 ID(传递给onStartCommand()的startId)。然后,如果在您能够调用stopSelf(int)之前服务收到了新的启动请求,ID 就不匹配,服务也就不会停止。

创建绑定服务:

绑定服务允许应用组件通过调用bindService()与其绑定,以便创建长期连接(通常不允许组件通过调用startService()启动它)。

如需与 Activity 和其他应用组件中的服务进行交互,或者需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。

要创建绑定服务,必须实现onBind()回调方法以返回IBinder,用于定义与服务通信的接口。然后,其他应用组件可以调用bindService()来检索该接口,并开始对服务调用方法。服务只用于与其绑定的应用组件,因此如果没有组件绑定到服务,则系统会销毁服务(您不必按通过onStartCommand()启动的服务那样来停止绑定服务)。

要创建绑定服务,首先必须定义指定客户端如何与服务通信的接口。 服务与客户端之间的这个接口必须是IBinder的实现,并且服务必须从onBind()回调方法返回它。一旦客户端收到IBinder,即可开始通过该接口与服务进行交互。

多个客户端可以同时绑定到服务。客户端完成与服务的交互后,会调用unbindService()取消绑定。一旦没有客户端绑定到该服务,系统就会销毁它。

实现生命周期回调


左图显示了使用startService()所创建的服务的生命周期,右图显示了使用bindService()所创建的服务的生命周期。

通过实现这些方法,您可以监控服务生命周期的两个嵌套循环:

整个生命周期:服务的整个生命周期从调用onCreate()开始起,到onDestroy()返回时结束。与 Activity 类似,服务也在onCreate()中完成初始设置,并在onDestroy()中释放所有剩余资源。例如,音乐播放服务可以在onCreate()中创建用于播放音乐的线程,然后在onDestroy()中停止该线程。
无论服务是通过startService()还是bindService()创建,都会为所有服务调用onCreate()onDestroy()方法。

有效生命周期:服务的有效生命周期从调用onStartCommand()onBind()方法开始。每种方法均有 {Intent对象,该对象分别传递到startService()bindService()
对于启动服务,有效生命周期与整个生命周期同时结束(即便是在onStartCommand()返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在onUnbind()返回时结束。

推荐阅读更多精彩内容