Service
是
应用组件
长时间运行的操作。它不提供界面。一次
一项服务可能会继续运行一段时间,即使用户切换到另一项服务
应用。此外,组件可以绑定到服务以与其进行交互,甚至执行
进程间通信 (IPC)。例如,服务可以处理网络事务、
音乐、执行文件 I/O 或与内容提供程序互动,所有这一切都可以在后台进行。
注意:服务在其宿主的主线程中运行 过程;服务不会创建自己的线程,也不会 在单独的进程中运行,除非另有说明。您应该对 单独的线程,以免应用 无响应 (ANR) 错误。
服务类型
以下是三种不同的服务类型:
- 前景
-
前台服务会执行一些可被 用户。例如,音频应用会使用前台服务来播放 音轨。前台服务必须显示通知。 即使用户没有互动,前台服务也会继续运行 。
使用前台服务时,您必须显示通知, 用户会主动知晓服务正在运行此通知不能 除非停止或将服务从 前景。
详细了解如何配置 前台服务 应用。
注意: WorkManager API 提供了一种灵活的任务调度方式, 能够 将这些作业视为前台服务。在许多情况下,使用 WorkManager 优于直接使用前台服务。
- 背景
- 后台服务执行的操作不会直接被
用户。例如,如果应用使用某项服务来压缩其存储空间,
(通常是后台服务)
注意:如果您的应用以 API 级别 26 或更高级别为目标,系统会对后台运行施加限制 服务。大多数 例如,您不应 从 Google 地图 背景。相反, 使用 YAML 文件 WorkManager 中。
- 绑定
- 当应用组件通过调用
bindService()
绑定到服务时,服务即处于“绑定”状态。绑定服务提供 该接口允许组件与服务交互、发送请求、接收结果、 甚至可以通过进程间通信 (IPC) 跨进程执行这些操作。绑定服务仅运行 只要另一个应用组件与该应用绑定即可多个组件可以绑定到 但全部取消绑定后,相应服务就会被销毁。
尽管本文档通常分别讨论已启动服务和绑定服务,
你的服务可以双向运行:既可以启动(无限期运行),也可以允许
绑定。唯一的问题在于您是否实现几种回调方法:onStartCommand()
(允许组件启动)和 onBind()
(允许绑定)。
无论您的服务是启动、绑定还是同时启动,任何应用组件
可以使用该服务(即使是来自单独的应用程序)使用方式与任何组件使用该服务的方式相同
一个 activity。通过使用 Intent
启动它。不过,您可以声明
在清单文件中将该服务设为 private,并阻止其他应用访问。
有关详情,请参阅在
清单。
在服务和线程之间进行选择
简单地说,服务是一种可在后台运行的组件,即使用户 因此,您应该创建服务 。
如果您必须在主线程之外执行操作,但仅在用户正在与用户互动时执行
您应改为在另一个应用的上下文中创建一个新线程
组件。例如,如果您只想在 activity 运行时播放一些音乐,
则可以在 onCreate()
中创建线程,
在onStart()
中开始运行
并在 onStop()
中将其停止。
还可以考虑使用 java.util.concurrent
软件包中的线程池和执行器
或 Kotlin 协程,而不是传统的
Thread
类。请参阅
Android 上的线程文档,详细了解
将执行移至后台线程。
请记住,如果使用服务,它仍会通过以下方法在应用的主线程中运行: 因此,如果该服务执行密集型或非密集型操作, 屏蔽操作
基础知识
如需创建服务,您必须创建 Service
的子类或使用一个
现有子类。在实现中,您必须替换一些回调方法,
处理服务生命周期的关键方面,并提供一种机制来让组件能够
绑定至服务(如果适用)。这些是您应该了解的最重要的回调方法
替换:
onStartCommand()
- 当另一个组件(如 activity)请求启动服务时,系统会通过调用
startService()
来调用此方法。 执行此方法时,服务会启动并可在 无限期显示背景。如果您实现此方法,则应负责在发生以下情况时停止该服务: 其工作将通过调用stopSelf()
或stopService()
完成。如果您只想提供绑定,则无需 您需要实现此方法 onBind()
- 当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用
bindService()
来调用此方法。 在此方法的实现中,您必须提供一个接口,供客户端 用于通过返回IBinder
与服务通信。您必须始终 实现此方法;不过,如果您不希望允许绑定,则应返回 null。 onCreate()
- 当服务处于活跃状态时,系统会调用此方法来执行一次性设置流程。
(在调用
onStartCommand()
或onBind()
)。如果服务已在运行,则此方法不会 调用。 onDestroy()
- 当不再使用服务并且要销毁该服务时,系统会调用此方法。 您的服务应实现此方法来清理任何资源,例如线程、已注册的 监听器或接收器。这是服务接收的最后一个调用。
如果组件通过调用 startService()
启动服务(这会导致调用 onStartCommand()
),则服务
会持续运行,直至通过 stopSelf()
或其他程序自行停止运行
组件会通过调用 stopService()
将其停止。
如果组件调用
bindService()
用于创建服务,并且系统未调用 onStartCommand()
,则服务会运行
但前提是该组件已绑定到该组件服务与其所有客户端解除绑定后
系统就会将其销毁
仅当内存不足且必须恢复系统时,Android 系统才会停止服务
具有用户焦点的 activity 的资源。如果服务绑定到具有用户
因此其被终止的可能性较低;如果服务声明为在前台运行,则很少会终止该服务。
如果服务已启动且长时间运行的服务,系统会降低服务的位置
持续出现在后台任务列表中,并且该服务变得很容易受到
终止 - 如果服务已启动,您必须将其设计为能够妥善处理重启
错误。如果系统终止了您的服务,它会在资源变为实际状态后立即重启该服务
但这也取决于您从 onStartCommand()
返回的值。更多信息
有关系统何时销毁服务的信息,请参阅进程和线程
文档。
在后续部分中,您将了解如何创建
startService()
和
bindService()
服务方法,以及如何使用
从其他应用组件中获取它们
使用清单文件声明服务
您必须在应用的 就像对 activity 和其他组件执行的操作一样。
如需声明您的服务,请添加 <service>
元素
作为 <application>
的子级
元素。示例如下:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
请参阅 <service>
元素
参考,详细了解如何在清单中声明服务。
您可以在 <service>
元素中添加其他属性,
定义属性,例如启动服务所需的权限和
服务应运行的位置。android:name
属性是唯一的必需属性,它指定服务的类名称。更新后
请不要修改此名称,以免破坏
而不需要再依赖显式 intent 来启动或绑定服务(请阅读博文《Things》
“无法改变”)。
注意:为确保您的应用安全无虞,请务必使用
显式 intent,Service
时不声明 intent 过滤器,
您的服务。使用隐式 Intent 启动服务存在安全隐患,因为
确定会响应该 Intent 的服务,但用户无法看到哪项服务
。从 Android 5.0(API 级别 21)开始,如果您调用
bindService()
替换为隐式 intent。
如需确保您的服务仅供您的应用使用,您可以执行以下操作
包括 android:exported
属性并将其设置为 false
。这可以有效阻止其他应用启动您的
(即使在使用显式 intent 的情况下)。
注意:
用户可以查看自己的设备上运行了哪些服务。如果他们看到
无法识别或信任的服务,则可以停止该服务。在
为了避免用户意外停止您的服务
添加
android:description
属性添加到
<service>
元素。在说明中
用一句简短的话说明服务的用途和优势
。
创建启动服务
启动服务是另一个组件通过调用 startService()
启动的服务,这会导致调用服务的
onStartCommand()
方法。
服务启动时,其生命周期独立于
组件。该服务可以在后台无限期运行,即使
启动它的组件将被销毁。因此,服务在作业运行时应自行停止运行。
通过调用 stopSelf()
完成更改,否则另一个组件可以
您可以调用 stopService()
来停止它。
应用组件(如 activity)可以通过调用 startService()
并传递 Intent
来启动服务
,用于指定服务并包含供服务使用的所有数据。该服务会收到
在 onStartCommand()
方法中设置此 Intent
。
例如,假设某个 activity 需要将一些数据保存到在线数据库。活动
可以启动配套服务,并通过向 startService()
传递一个 intent 来提供要保存的数据。服务在 onStartCommand()
中接收 intent,连接到互联网,然后执行
数据库事务。事务完成后,服务将自行停止运行,
已销毁。
注意:服务与应用在同一进程中运行 并且默认情况下位于该应用的主线程中。如果您的服务 当用户与使用同一 activity 的 activity 进行交互时,系统会执行密集型操作或阻塞性操作 应用,该服务会降低 activity 性能。为避免影响应用 请在服务内启动新线程。
Service
类是基础类
类。扩展此类时,请务必创建一个新线程
该服务可以完成所有工作;该服务会通过以下方式使用应用的主线程:
否则会降低应用正在运行的所有 activity 的性能。
Android 框架还提供了 IntentService
Service
的子类,该类使用
工作器线程来处理所有启动请求,一次一个。使用此类
建议为新应用提供,因为它从 Android 8 Oreo 开始无法正常运行,因为
推出了后台执行限制。
此外,从 Android 11 开始,它就已废弃。
您可以将 JobIntentService 用作
取代了与较新版本 Android 兼容的 IntentService
。
以下部分介绍了如何实现自己的自定义服务, 对于大多数用例,强烈建议改用 WorkManager。参阅 Android 上的后台处理指南 看看是否有满足您需求的解决方案。
扩展服务类
您可以扩展 Service
类。
处理每个传入的 intent。基本实现如下所示:
Kotlin
class HelloService : Service() { private var serviceLooper: Looper? = null private var serviceHandler: ServiceHandler? = null // Handler that receives messages from the thread private inner class ServiceHandler(looper: Looper) : Handler(looper) { override fun handleMessage(msg: Message) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000) } catch (e: InterruptedException) { // Restore interrupt status. Thread.currentThread().interrupt() } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1) } } override fun onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply { start() // Get the HandlerThread's Looper and use it for our Handler serviceLooper = looper serviceHandler = ServiceHandler(looper) } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show() // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job serviceHandler?.obtainMessage()?.also { msg -> msg.arg1 = startId serviceHandler?.sendMessage(msg) } // If we get killed, after returning from here, restart return START_STICKY } override fun onBind(intent: Intent): IBinder? { // We don't provide binding, so return null return null } override fun onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show() } }
Java
public class HelloService extends Service { private Looper serviceLooper; private ServiceHandler serviceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore interrupt status. Thread.currentThread().interrupt(); } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work doesn't disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler serviceLooper = thread.getLooper(); serviceHandler = new ServiceHandler(serviceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = serviceHandler.obtainMessage(); msg.arg1 = startId; serviceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } }
示例代码会处理 onStartCommand()
中的所有来电
并将工作发布到在后台线程上运行的 Handler
。它的工作方式与 IntentService
类似,会依次逐个处理所有请求。
例如,如果您想同时运行多个请求,可以通过更改代码在线程池上运行工作。
请注意,onStartCommand()
方法必须返回
整数。该整数是一个值,用于说明系统应如何在
事件。返回值
来自“onStartCommand()
”的类型必须是以下项之一:
常量:
START_NOT_STICKY
- 如果系统在
onStartCommand()
返回后终止服务,除非有待处理的服务,否则不要重新创建服务 传递的意向。这是最安全的选项,可避免在不必要时运行服务 以及何时您的应用可以简单地重启任何未完成的作业。 START_STICKY
- 如果系统在
onStartCommand()
返回后终止服务,请重新创建服务并调用onStartCommand()
,但不要重新提交最后一个 intent。 相反,系统会调用包含onStartCommand()
null intent,除非有用于启动服务的待处理 intent。在这种情况下 这些 intent 会被传递。这适用于非 执行命令,但无限期运行并等待作业。 START_REDELIVER_INTENT
- 如果系统在
onStartCommand()
返回后终止服务,则重新创建服务并使用传递给onStartCommand()
服务。所有待定 intent 均依次传递。这适用于 主动执行应立即恢复的作业(例如下载文件)。
如需详细了解这些返回值,请参阅链接的参考文档 每个常量的文档。
启动服务
您可以通过以下方式从 activity 或其他应用组件启动服务:
传递 Intent
至 startService()
或 startForegroundService()
。通过
Android 系统会调用服务的 onStartCommand()
方法并向其传递 Intent
,
用于指定要启动的服务。
注意:如果您的应用以 API 级别 26 或更高级别为目标平台,系统会
对使用或创建后台服务施加限制,除非应用
位于前台。如果应用需要创建前台服务,
应用应调用 startForegroundService()
。该方法会创建一个后台服务,但是
方法会向系统发出信号,表明该服务会将自身提升到
前景。创建服务后,该服务必须调用其
startForeground()
方法的范围
五秒钟。
例如,activity 可以通过结合使用显式 intent 和 startService()
来启动上一部分中的示例服务 (HelloService
),如下所示:
Kotlin
startService(Intent(this, HelloService::class.java))
Java
startService(new Intent(this, HelloService.class));
startService()
方法会立即返回,并且
Android 系统会调用服务的 onStartCommand()
方法。如果服务尚未运行,则系统会先调用 onCreate()
,然后再调用
onStartCommand()
。
如果服务也未提供绑定,则使用 startService()
传递的 intent 是
应用组件和服务不过,如果您希望该服务发回结果
启动服务的客户端可以为广播创建 PendingIntent
(使用 getBroadcast()
)并将其传递给服务
在用于启动服务的 Intent
中。然后,该服务便可使用
广播以提供结果。
多个服务启动请求会导致多次对服务的
onStartCommand()
。但是,只有一个
需要该服务(具有 stopSelf()
或 stopService()
)才能将其停止。
停止服务
启动的服务必须管理自己的生命周期。也就是说,系统不会停止或停止
除非必须回收系统内存和服务
在 onStartCommand()
返回后继续运行。通过
服务必须通过调用 stopSelf()
或其他方法自行停止运行
组件可以通过调用 stopService()
来停止它。
一旦请求使用 stopSelf()
或 stopService()
停止服务,系统就会立即销毁服务。
如果您的服务同时处理对 onStartCommand()
的多个请求,则不应停止
因为您可能收到了新的
启动请求(在第一个请求结束时停止会终止第二个请求)。为避免
则可以使用 stopSelf(int)
来确保请求
始终以最近的启动请求为基础来停止服务。也就是说,当您调用 stopSelf(int)
时,会传递启动请求的 ID(即 startId
发送至 onStartCommand()
)
。然后,如果服务在您能够调用 stopSelf(int)
之前收到新的启动请求,则 ID 不匹配,服务也不会停止。
注意:为避免浪费系统资源和
电池电量,请确保您的应用程序在工作完成后停止其服务。
如有必要,其他组件可以通过调用 stopService()
来停止服务。即使您为服务启用了绑定
如果服务收到对 onStartCommand()
的调用,您必须始终自行停止服务。
如需详细了解服务生命周期,请参阅下文中的管理服务生命周期部分。
创建绑定服务
绑定服务允许应用组件通过调用 bindService()
与其绑定,以创建长期连接。
它通常不允许组件通过调用 startService()
来启动它。
当您想要通过 activity 与该服务进行交互时,请创建绑定服务 或向应用提供部分功能, 以便通过进程间通信 (IPC) 向其他应用发起通信。
如需创建绑定服务,请实现 onBind()
回调方法以返回 IBinder
,
定义了与服务通信的接口。然后,其他应用组件可以调用
bindService()
用于检索接口;
开始对服务调用方法。该服务仅用于为
因此,当没有组件绑定到服务时,系统会将其销毁。
您不需要像停止绑定服务时那样,停止绑定服务
从 onStartCommand()
开始。
如需创建绑定服务,您必须定义指定客户端如何才能
与服务通信服务之间的此接口
客户端必须是 IBinder
的实现,并且是您的服务必须实现的
从 onBind()
回调方法返回。客户端收到 IBinder
后,可以开始
与服务交互
多个客户端可以同时绑定到服务。当客户端完成与
服务,它会调用 unbindService()
来取消绑定。
如果没有绑定到服务的客户端,则系统会销毁该服务。
实现绑定服务的方式有多种, 比启动服务更复杂出于这些原因,绑定服务讨论会出现在 有关绑定服务的单独文档。
向用户发送通知
服务在运行时,可以使用信息条通知或状态栏通知向用户通知事件。
信息条通知是一种消息,只会在当前窗口的表面显示 然后就消失了。状态栏通知会在状态栏中提供图标(带有 消息,用户可以选择此消息以执行操作(例如启动 Activity)。
通常,状态栏通知是后台工作(例如 文件下载已完成,用户现在可以对其执行操作。当用户 从展开视图中选择通知时,通知可以启动 activity (例如,显示下载的文件)。
管理服务的生命周期
服务的生命周期比 activity 的生命周期简单得多。但更重要的是 请务必密切关注服务的创建和销毁方式,因为 服务可以在用户不知情的情况下在后台运行。
服务生命周期(从创建到销毁)可以遵循 以下两个路径之一:
- 启动服务
该服务在其他组件调用
startService()
时创建。然后无限期运行 通过调用stopSelf()
自行停止运行。另一个组件也可以停止 调用stopService()
的服务。服务停止后,系统会将其销毁。 - 绑定服务
该服务在其他组件(客户端)调用
bindService()
时创建。然后,客户端与服务进行通信 通过IBinder
接口实现。客户端可以通过调用unbindService()
。多个客户端可以绑定到 同一个服务,当所有这些服务都解除绑定后,系统就会销毁该服务。服务 无需自行停止。
这两条路径并非完全独立。你可以绑定到一个
开头是 startService()
。例如,您可以
使用 Intent
(用于标识要播放的音乐)调用 startService()
,从而启动后台音乐服务。稍后,
例如,当用户想要对播放器进行一定程度的控制或获取有关
当前歌曲,activity 可以通过调用 bindService()
绑定到该服务。在这种情况下,除非所有客户端都取消绑定,否则 stopService()
或 stopSelf()
不会实际停止服务。
实现生命周期回调
与 activity 一样,服务具有生命周期回调方法,您可以实现这些方法来监控 服务状态的变化,并在适当的时间执行工作。以下骨架 服务演示了每种生命周期方法:
Kotlin
class ExampleService : Service() { private var startMode: Int = 0 // indicates how to behave if the service is killed private var binder: IBinder? = null // interface for clients that bind private var allowRebind: Boolean = false // indicates whether onRebind should be used override funonCreate
() { // The service is being created } override funonStartCommand
(intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override funonBind
(intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override funonUnbind
(intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override funonRebind
(intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override funonDestroy
() { // The service is no longer used and is being destroyed } }
Java
public class ExampleService extends Service { int startMode; // indicates how to behave if the service is killed IBinder binder; // interface for clients that bind boolean allowRebind; // indicates whether onRebind should be used @Override public voidonCreate
() { // The service is being created } @Override public intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return startMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return binder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return allowRebind; } @Override public voidonRebind
(Intent intent) { // A client is binding to the service withbindService()
, // after onUnbind() has already been called } @Override public voidonDestroy
() { // The service is no longer used and is being destroyed } }
注意:与 activity 生命周期回调方法不同, 不需要调用这些回调方法的父类实现。
图 2 说明了典型的服务回调方法。虽然这个数字与
startService()
通过这些服务创建的
创建者:bindService()
,保留
请注意,任何服务,无论启动方式如何,都可能允许客户端与之绑定。
最初使用 onStartCommand()
启动的服务(由客户端调用 startService()
启动)
仍然可以接收对 onBind()
的调用(当客户端调用
bindService()
)。
通过实现这些方法,您可以监控服务 生命周期:
- 服务的整个生命周期从调用
onCreate()
到onDestroy()
返回之间结束。与 activity 一样,服务在onCreate()
并释放onDestroy()
中的所有剩余资源。例如, 音乐播放服务可以在onCreate()
中创建用于播放音乐的线程,然后可以在onDestroy()
中停止该线程。注意:
onCreate()
和onDestroy()
方法,系统会针对所有服务调用该方法,无论 它们是由startService()
或bindService()
创建的。 - 服务的有效生命周期始于对
onStartCommand()
或onBind()
的调用。 每个方法都会获得传递给startService()
或bindService()
的Intent
。如果启动了服务,则有效生命周期与整个生命周期同时结束 结束(即使在
onStartCommand()
返回之后,服务仍然处于活动状态)。如果服务已绑定,则有效生命周期在onUnbind()
返回时结束。
注意:尽管启动服务是通过调用
stopSelf()
或 stopService()
,则不存在针对
服务(没有 onStop()
回调)。除非该服务绑定到客户端
当服务停止时,系统会将其销毁 - onDestroy()
是收到的唯一回调。
如需详细了解如何创建提供绑定的服务,请参阅绑定服务文档。
其中包含有关onRebind()
管理
绑定服务。