安卓服务类是专门设计来允许应用启动和执行后台任务的。与广播接收器旨在快速执行任务然后退出不同,服务旨在执行需要很长时间才能完成的任务(例如通过互联网连接下载文件或向用户流式传输音乐),但不需要用户界面。
本章将概述不同类型的可用服务,包括已启动服务、绑定服务和意向服务。一旦涵盖了这些基础知识,后面的章节将通过一些实际服务的例子进行阐述。
58.1 开始服务秒
启动的服务由其他应用组件(如活动甚至广播接收器)启动,并可能在后台无限期运行,直到服务停止,或者被安卓运行时系统销毁以释放资源。如果启动服务的应用不再处于前台,甚至在最初启动服务的组件被破坏的情况下,服务也将继续运行。
默认情况下,服务将在与启动它的应用进程相同的主线程中运行(称为本地服务)。因此,重要的是,任何 CPU 密集型任务都要在服务中的新线程中执行。指示服务在单独的进程中运行(因此称为远程服务)需要清单文件中的配置更改。
除非服务被特别配置为私有(再次通过清单文件中的设置),否则该服务可以由同一安卓设备上的其他组件启动。这是通过使用意图机制来实现的,就像前面章节中概述的那样,一个活动可以启动另一个活动。
已启动的服务通过调用 startService()方法启动,传递一个标识要启动的服务的 Entity 对象作为参数。当一个已启动的服务完成了它的任务,它应该通过调用 stopSelf()来停止自己。或者,另一个组件可以通过调用 stopService()方法来停止正在运行的服务,将匹配的服务停止意图作为参数传递。
服务被安卓系统赋予了很高的优先级,并且通常是为了释放资源而最后终止的服务。
58.2 意向服务
如前所述,默认情况下,服务在与启动它们的组件相同的主线程中运行。因此,需要由服务执行的任何 CPU 密集型任务都应该在新线程中进行,从而避免影响调用应用的性能。
IntentService 类是一个便利类(服务类的子类),它设置了一个工作线程来处理后台任务,并以异步方式处理每个请求。一旦服务处理完所有排队的请求,它就退出。当使用 IntentService 类时,所需要的只是实现 onHandleIntent()方法,其中包含要为每个请求执行的代码。
对于不需要同步处理请求的服务,建议选择 IntentService。然而,需要同步处理请求的服务将需要从服务类中子类化,并手动实现和管理线程,以高效地处理任何 CPU 密集型任务。
58.3 绑定服务
绑定服务类似于启动的服务,只是启动的服务通常不返回结果或不允许与启动它的组件交互。另一方面,绑定服务允许启动组件与服务交互,并从服务接收结果。通过进程间通信(IPC)的实现,这种交互也可以跨进程边界发生。例如,一项活动可能会启动一项服务来处理音频回放。该活动很可能包括一个用户界面,为用户提供控制,以便暂停回放或跳到下一首曲目。类似地,该服务很可能需要向呼叫活动传递信息,以指示当前音频轨道已经完成,并提供即将开始播放的下一个轨道的细节。
组件(在本文中也称为客户端)通过调用 bindService()方法启动并绑定到绑定的服务。此外,多个组件可能同时绑定到一个服务。当客户端不再需要服务绑定时,应该调用 unbindService()方法。当最后一个绑定的客户端从服务解除绑定时,服务将被安卓运行时系统终止。请务必记住,绑定服务也可以通过调用 startService()来启动。一旦启动,组件就可以通过 bindService()调用绑定到它。当绑定服务通过调用 startService()启动时,它将继续运行,即使在最后一个客户端解除绑定后也是如此。
绑定的服务必须包含 onBind()方法的实现,该方法在服务最初创建时以及其他客户端随后绑定到正在运行的服务时都会被调用。该方法的目的是向绑定客户端返回一个 IBinder 类型的对象,该对象包含客户端与服务通信所需的信息。
就实现客户端和绑定服务之间的通信而言,推荐的技术取决于客户端和服务是否驻留在相同或不同的进程中,以及服务是否对客户端是私有的。本地通信可以通过扩展 Binder 类并从 onBind()方法返回一个实例来实现。另一方面,进程间通信需要 Messenger 和 Handler 实现。这两种方法的细节将在后面的章节中介绍。
58.4 服务的剖析
如前所述,服务必须被创建为安卓服务类(更具体地说是安卓. app.Service)的子类或其子类(如安卓. app.IntentService)。作为子类化过程的一部分,必须重写以下一个或多个超类回调方法,具体取决于所创建服务的确切性质:
onStartCommand()–这是另一个组件通过调用 startService()方法启动服务时调用的方法。对于绑定服务,不需要实现此方法。
onBind()–当组件通过调用 bindService()方法绑定到服务时调用。当实现绑定服务时,这个方法必须返回一个 IBinder 对象,以方便与客户端的通信。对于已启动的服务,必须实现此方法才能返回空值。
onCreate()–作为执行初始化任务的位置,该方法在调用 onStartCommand()或第一次调用 onBind()方法之前立即被调用。
onDestroy()–在服务被销毁时调用。
现有量()–仅适用于内部服务子类。调用此方法来处理服务的处理。它在独立于主应用的线程中执行。
请注意,IntentService 类包含自己的 onStartCommand()和 onBind()回调方法的实现,因此这些方法不需要在子类中实现。
58.5 控制被破坏的服务重启选项
安吉星命令()回调方法需要返回一个整数值,以定义当服务被安卓运行时系统破坏时应该发生什么。这些方法的可能返回值如下:
START _ NOT _ STICKY–向系统指示,如果服务被破坏,则不应重新启动服务,除非有待定的意向等待交付。
START _ STICKY–如果破坏发生在 onStartCommand()方法返回之后,则表示服务在被破坏后应尽快重新启动。如果没有挂起的意图等待传递,则使用空意图值调用 onStartCommand()回调方法。服务被销毁时正在处理的意图被丢弃。
START _ REDELIVER _ INTENT–表示如果服务在从 onStartCommand()回调方法返回后被销毁,服务应该重新启动,当前的意图重新传递给 onStartCommand()方法,然后是任何挂起的意图。
58.6 在清单文件中声明服务
为了使服务可用,必须首先在清单文件中声明它。这包括将适当配置的元素嵌入到现有的 条目中。至少,<服务> 元素必须包含声明服务类名的属性,如以下 XML 片段所示:
.
.
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="MyService>
</service>
</application>
</manifest>
默认情况下,服务被声明为公共的,因为它们可以被它们所在的应用包之外的组件访问。为了使服务私有,安卓:导出的属性必须在清单文件的<服务>元素中声明为假。例如:
<service android:name="MyService"
android:exported="false">
</service>
如前所述,默认情况下,服务在与调用组件相同的进程中运行。为了强制服务在其自己的进程内运行,向<服务>元素添加一个 android:process 属性,声明一个以冒号(:)为前缀的进程名称:
<service android:name="MyService"
android:exported="false"
android:process=":myprocess">
</service>
冒号前缀表示新进程对于本地应用是私有的。但是,如果流程名称以小写字母而不是冒号开头,则该流程将是全局的,可供其他组件使用。
最后,使用为活动概述的相同意图过滤机制,服务还可以向设备上运行的其他应用通告能力。有关意图过滤器的更多详细信息,请参考标题为“安卓意图概述”的章节。
58.7 启动服务在系统启动时运行
鉴于服务的后台性质,当基于安卓的系统首次启动时,需要启动服务的情况并不少见。这可以通过创建一个带有意图过滤器的广播接收器来实现,该过滤器被配置为监听系统 Android . intent . action . BOOT _ COMPLETED 意图。当检测到这样的意图时,广播接收器将简单地调用必要的服务,然后返回。请注意,为了运行,这样的广播接收器需要请求 Android . permission . RECEIVE _ BOOT _ COMPLETE 权限。
58.8 总结
安卓服务是一种强大的机制,允许应用在后台执行任务。服务一旦启动,将继续运行,不管调用应用是否是前台任务,甚至在启动服务的组件被破坏的情况下。
服务是安卓服务类的子类,属于已启动服务或绑定服务的范畴。已启动的服务会一直运行,直到被停止或销毁,并且不会固有地提供与其他组件进行交互或数据交换的机制。另一方面,绑定服务为其他客户端组件提供通信接口,并且通常运行到最后一个客户端从服务中解除绑定。
默认情况下,服务在与调用应用相同的进程和主线程中本地运行。因此,为了处理 CPU 密集型任务,应该在服务中创建一个新线程。通过对应用清单文件中相应的条目进行较小的配置更改,可以在单独的进程中启动远程服务。
IntentService 类(本身是 Android 服务类的子类)提供了一种方便的机制,用于在单独的工作线程中处理异步服务请求。