Android服务基础

服务概述

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

以下是三种不同类型的服务:

1.前台服务-Foreground

前台服务执行一些用户可以注意到的操作。例如,音频应用程序将使用前台服务播放音频轨。前台服务必须显示通知。即使用户不与应用程序交互,前台服务也会继续运行。

2.后台服务-Background

后台服务执行用户不会直接注意到的操作。例如,如果一个应用程序使用一个服务来压缩它的存储,那么它通常是一个后台服务。

  • 注意:如果您的应用程序的目标API级别为26或更高,当应用程序本身不在前台时,系统会对运行后台服务施加限制。在大多数情况下,您的应用程序应该使用计划好的作业。

3.绑定-Bound

当应用程序组件通过调用bindService()绑定到服务时,服务将被绑定。绑定服务提供一个客户机-服务器接口,该接口允许组件与服务交互、发送请求、接收结果,甚至通过进程间通信(IPC)跨进程进行交互。绑定服务仅在另一个应用程序组件绑定到它时才运行。多个组件可以同时绑定到服务,但是当所有组件都解除绑定时,服务将被销毁。


尽管本文档通常单独讨论启动和绑定服务,但您的服务可以以两种方式工作——它可以启动(无限期运行),并且还允许绑定。这只是一个简单的问题,您是否实现了两个回调方法:onStartCommand()允许组件启动它,onBind()允许绑定。

无论您的服务是启动、绑定还是同时启动,任何应用程序组件都可以使用该服务(即使是从单独的应用程序中),方式与任何组件使用活动的方式相同——通过有目的地启动它。但是,您可以在清单文件中将服务声明为私有,并阻止来自其他应用程序的访问。关于在清单中声明服务的部分将对此进行更多讨论。


  • 注意:
    服务在其宿主进程的主线程中运行;服务不会创建自己的线程,也不会在单独的进程中运行,除非您指定了其他方式。如果您的服务要执行任何cpu密集型的工作或阻塞操作,比如MP3播放或联网,那么您应该在服务中创建一个新线程来完成这项工作。通过使用单独的线程,可以降低应用程序没有响应(ANR)错误的风险,并且应用程序的主线程可以保持与活动的用户交互。

在服务和线程之间进行选择

服务只是一个可以在后台运行的组件,即使用户没有与应用程序交互,也可以运行该组件,因此只有在需要服务时才应该创建服务。

如果必须在主线程之外执行工作,但是只在用户与应用程序交互时执行,则应该创建一个新线程。例如,如果您想播放一些音乐,但只是在活动运行时,您可以在onCreate()中创建一个线程,在onStart()中开始运行它,并在onStop()中停止它。还可以考虑使用AsyncTask或HandlerThread代替传统的Thread类。有关线程的更多信息,请参阅进程和线程文档。

请记住,如果您确实使用了服务,默认情况下它仍然在应用程序的主线程中运行,所以如果服务执行密集或阻塞操作,您仍然应该在服务中创建一个新线程。

基础

要创建服务,您必须创建服务的子类或使用其现有子类之一。在实现中,必须覆盖一些回调方法,这些方法处理服务生命周期的关键方面,并提供一种机制,允许组件在适当的情况下绑定到服务。这些是你应该覆盖的最重要的回调方法:

1. onStartCommand ()
当另一个组件(例如一个活动)请求启动服务时,系统通过调用startService()调用此方法。当此方法执行时,将启动服务并可以在后台无限期运行。如果您实现了这一点,那么您有责任通过调用stopSelf()或stopService()来在服务完成工作时停止服务。如果只想提供绑定,则不需要实现此方法。

2. onBind ()
当其他组件希望与服务绑定(例如执行RPC)时,系统通过调用bindService()来调用此方法。在此方法的实现中,必须提供一个接口,客户端使用该接口通过返回IBinder与服务进行通信。您必须始终实现此方法;但是,如果不允许绑定,则应该返回null。

3. onCreate ()
当服务最初创建时(在调用onStartCommand()或onBind()之前),系统调用此方法执行一次性设置过程。如果服务已经运行,则不调用此方法。

4. onDestroy ()
当服务不再使用并正在被销毁时,系统将调用此方法。您的服务应该实现此功能来清理任何资源,例如线程、注册侦听器或接收器。这是服务接收的最后一个调用。


如果一个组件通过调用startService()来启动服务(这会导致调用onStartCommand()),那么服务将继续运行,直到它使用stopSelf()停止自身,或者另一个组件通过调用stopService()停止服务。

如果组件调用bindService()来创建服务,而不调用onStartCommand(),则服务仅在组件绑定到它时才运行。当服务从所有客户端解除绑定后,系统将其销毁。

Android系统只在内存不足时停止服务,并且必须为具有用户焦点的活动恢复系统资源。如果服务绑定到具有用户焦点的活动,则不太可能被终止;如果服务被声明为在前台运行,它很少被杀死。如果服务已经启动并且是长时间运行的,那么随着时间的推移,系统在后台任务列表中的位置会降低,并且服务非常容易被终止——如果您的服务已经启动,那么您必须将其设计为能够优雅地处理系统的重新启动。如果系统终止了您的服务,它将在资源可用时立即重新启动服务,但这也取决于您从onStartCommand()返回的值。有关系统何时可能破坏服务的更多信息,请参见流程和线程文档。

在接下来的部分中,您将看到如何创建startService()和bindService()服务方法,以及如何从其他应用程序组件使用它们。


清单中声明服务

您必须在应用程序的清单文件中声明所有服务,就像您为活动和其他组件所做的那样。
要声明您的服务,请添加一个元素作为元素的子元素。举个例子:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

有关在清单中声明服务的更多信息,请参阅元素引用。

您还可以在元素中包含其他属性,以定义启动服务所需的权限和服务应该在其中运行的流程等属性。android:name属性是惟一必需的属性——它指定服务的类名。发布应用程序之后,保持这个名称不变,以避免由于依赖于启动或绑定服务的显式意图而破坏代码的风险(请阅读博客文章,无法更改的内容)。

  • 注意:为了确保您的应用程序是安全的,在启动服务时始终使用显式意图,并且不要为您的服务声明意图过滤器。使用隐式意图启动服务是一种安全风险,因为您不能确定响应该意图的服务,用户也不能看到哪个服务启动了。从Android
    5.0 (API level 21)开始,如果您使用隐式意图调用bindService(),系统将抛出异常。

您可以通过包含android:export属性并将其设置为false来确保您的服务只对您的应用程序可用。这有效地阻止了其他应用程序启动您的服务,即使使用明确的意图。

  • 注意:用户可以看到他们的设备上运行的是什么服务。如果他们看到一个他们不认识或不信任的服务,他们可以停止该服务。为了避免用户意外停止服务,您需要在应用程序清单中的元素中添加android:description属性。在描述中,提供一个简短的句子,解释服务的功能及其提供的好处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值