Android Service回调和配置
- 《Android Service基础》
- 《Android Service回调和配置》
- 《Android Service aidl使用及进阶》
- 《Android Service更多知识》
- 《Android 中的 IntentService 类详解》
- 《Android Service aidl分析》
- 《Android Service 流程分析》
Service 生命周期方法回调
创建服务,必须创建 Service 的子类(或使用它的一个现有子类)。可以重写一些回调方法,从而处理服务生命周期的某些关键方面,主要的回调方法有 onCreate()
、onBind()
、onRebind()
、onStartCommand()
、onStart()
、onUnbind()
、onDestroy()
。
onCreate()
首次创建服务时,系统会(在调用 onStartCommand()
或 onBind()
之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。
onBind()
当另一个组件想要与服务绑定时,系统会通过调用 bindService()
来调用此方法。在此方法的实现中,必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。但是,如果并不希望服务允许绑定,则应返回 null。
onRebind()
服务未被销毁,再次绑定时回调。前提是 onUnbind()
方法返回true(如果服务已启动并接受绑定,则当系统调用 onUnbind()
方法时,如果想在客户端下一次绑定到服务时接收 onRebind()
调用,则可选择返回 true)。onRebind()
返回空值,但客户端仍在其 onServiceConnected()
回调中接收 IBinder。
onStartCommand()
当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService()
来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。如果实现此方法,则在服务工作完成后,需负责通过调用 stopSelf()
或 stopService()
来停止服务。(如果只想提供绑定,则无需实现此方法)。
onStart()
该方法已过时,使用 onStartCommand()
回调即可。
onUnbind()
当所有客户端都和服务解除绑定时调用。解除绑定方法 context.unbindService()
。
onDestroy()
当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理资源,如线程、注册的监听器、接收器等。这是服务接收的最后一个调用。
Service 生命周期流程图
服务生命周期图
服务生命周期图左图显示使用 startService() 创建的服务的生命周期,右图显示使用 bindService() 创建的服务的生命周期
- 服务的整个生命周期贯穿调用
onCreate()
和返回onDestroy()
之间的这段时间。与 Activity 类似,服务也在onCreate()
中完成初始设置,并在onDestroy()
中释放所有剩余资源。 - 服务的活动生命周期从调用
onStartCommand()
或onBind()
开始。每种方法均会获得 Intent 对象,该对象由startService()
或bindService()
方法传递。 - 对于启动服务,活动生命周期与整个生命周期会同时结束(即便是在
onStartCommand()
返回之后,服务仍然处于活动状态)。对于绑定服务,活动生命周期会在onUnbind()
返回时结束。 - 尽管可以通过调用
stopSelf()
或stopService()
来停止绑定服务,但该服务并没有相应的回调(如 onStop() 回调)。除非服务绑定到客户端(解绑回调onUnbind()
),否则在服务停止时,系统会将其销毁(onDestroy()
是接收到的唯一回调)。
已启动且允许绑定的服务的生命周期
已启动且允许绑定的服务的生命周期-
onRebind()
方法会回调的前提是:服务未被销毁,再次绑定时回调,并且onUnbind()
方法返回true。 -
onRebind()
返回空值,但客户端仍会回调ServiceConnection
的onServiceConnected()
方法,接收IBinder。
Service 部分相关方法详细说明
onStartCommand()
当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService()
来调用此方法。
参数:
- Intent intent:调用
stopService(Intent name)
方法传递的 Intent 对象,组件可以通过Intent传递参数给Service,如果之后服务重新启动,可能为null,具体是否需要有数据,需要根据该方法的返回值确定。 - int flags:其他附加数据。
- int startId:表示要启动的特定请求的唯一整数。一般与
stopSelfResult(int)
一起使用。
返回值:
一般来说,主要有三类返回值
-
START_STICKY:如果系统在
onStartCommand()
返回后终止服务,则其会重建服务并调用onStartCommand()
,但不会重新传递最后一个 Intent。如果还有挂起 Intent 要启动服务,系统会传递这些 Intent;否则系统会调用包含空 Intent 的 onStartCommand()。此常量适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。 - START_STICKY_COMPATIBILITY:START_STICKY 的兼容版本。
-
START_NOT_STICKY:如果系统在
onStartCommand()
返回后终止服务,除非有待传递的挂起的 Intent,否则系统不会重建服务。 -
START_REDELIVER_INTENT:如果系统在
onStartCommand()
返回后终止服务,则其会重建服务,并通过传递给服务的最后一个 Intent 调用onStartCommand()
。所有挂起 Intent 均依次传递。此常量适用于主动执行应立即恢复的作业(例如下载文件)的服务。
bindService()
用于绑定服务的方法
参数:
- Intent service:意图,标识要连接的服务。(注意:如果使用 Intent 来绑定到 Service,请务必使用显式 Intent 来确保应用的安全性。使用隐式 Intent 启动服务存在安全隐患,因为无法确定哪些服务会响应该 Intent,并且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),则系统会抛出异常。)
- ServiceConnection conn:
ServiceConnection
对象,在在服务启动和断开连接时接收信息,不能为null - int flags:指示绑定选项的标记。如要创建尚未处于活动状态的服务,此参数应为
BIND_AUTO_CREATE
(常用)。其他可能的值为BIND_DEBUG_UNBIND
(主要用于调试)、BIND_NOT_FOREGROUND
等,或者 0(表示无此参数)。 - 实现
ServiceConnection
必须重写两个回调方法:-
onServiceConnected()
:系统会调用该方法,进而传递服务的onBind()
方法所返回的IBinder
-
onServiceDisconnected()
:当与服务的连接意外中断(例如服务崩溃或被终止)时,Android 系统会调用该方法。当客户端取消绑定时,系统不会调用该方法。
-
onUnbind()
当所有客户端都和服务解除绑定时调用。如果返回true,当服务未销毁并重新调用 bindService()
时回调 onRebind()
和 ServiceConnection
的 onServiceConnected()
方法;否则,只会回调 ServiceConnection
的 onServiceConnected()
方法。
startForeground()
/ stopForeground()
startForeground()
:让服务运行在前台,参数1:通知标识(不能为 0)参数2:用于状态栏的 Notification
stopForeground()
:移出前台运行的服务,参数表示是否需同时移除状态栏通知。此方法并不会停止服务。但是,如果在服务运行于前台时将其停止,则通知也会随之移除。
startForegroundService()
在Android 8.0 以上系统不允许后台应用创建后台服务。 因此引入了一种全新的方法,即通过 Context.startForegroundService() 方法在前台启动新服务,通过调用该方法,在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知,如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。
清单文件中 Service 配置
语法
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:foregroundServiceType=["connectedDevice" | "dataSync" |
"location" | "mediaPlayback" | "mediaProjection" |
"phoneCall"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>
具体说明
android:description
向用户描述服务的字符串。应将此标签设置为对字符串资源的引用,以便可以像对界面中的其他字符串那样对其进行本地化。
android:directBootAware
服务是否支持直接启动,即其是否可以在用户解锁设备之前运行。默认值为 "false"。在直接启动期间,应用中的服务仅可访问存储在设备保护存储区的数据。
android:enabled
系统是否可实例化服务 —“true”表示可以,“false”表示不可以。默认值为“true”。
<application> 元素拥有自己的 enabled 属性,该属性适用于所有应用组件,包括服务。只有在 <application> 和 <service> 属性都为“true”(因为它们都默认使用该值)时,系统才能启用服务。任何一项为“false”都会造成服务停用,从而使系统无法将其实例化。
android:exported
其他应用的组件是否能调用服务或与之交互 —“true”表示可以,“false”表示不可以。当该值为“false”时,只有同一个应用或具有相同用户 ID 的应用的组件可以启动服务或绑定到服务。
默认值取决于服务是否包含 Intent 过滤器。没有任何过滤器意味着服务只能通过指定其确切的类名称进行调用。这意味着服务专供应用内部使用(因为其他应用不知晓其类名称)。因此,在这种情况下,默认值为“false”。另一方面,至少存在一个过滤器意味着服务专供外部使用,因此默认值为“true”。此属性并非是唯一限制向其他应用披露服务的方式。还可使用权限来限制哪些外部实体可以与服务交互。
android:foregroundServiceType
阐明服务是满足特定用例要求的前台服务。例如,"location" 类型的前台服务表示应用正在获取设备的当前位置,目的通常是继续用户发起的操作,且该操作与设备位置相关。可以将多个前台服务类型分配给特定服务。
android:icon
表示服务的图标。必须将该属性设置为对包含图像定义的可绘制资源的引用。如果未设置该属性,则转而使用为应用整体指定的图标。
android:isolatedProcess
如果设置为 true,则此服务将在与系统其余部分隔离的特殊进程下运行。此服务自身没有权限,只能通过 Service API 与其进行通信(绑定和启动)。
android:label
可向用户显示的服务名称。如果未设置该属性,则转而使用为应用整体设置的标签。
android:name
实现服务的 Service 子类的名称。此名称应为完全限定类名称(例如“com.example.project.RoomService”)。一旦发布应用,即不应更改该名称(除非设置了 android:exported="false")。没有默认值。必须指定该名称。
android:permission
实体启动服务或绑定到服务所必需的权限的名称。如果 startService()、bindService() 或 stopService() 的调用者尚未获得此权限,该方法将不起作用,且系统不会将 Intent 对象传送给服务。如果未设置该属性,则对服务应用由 <application> 元素的 permission 属性所设置的权限。如果二者均未设置,则服务不受权限保护。
android:process
将运行服务的进程的名称。正常情况下,应用的所有组件都会在为应用创建的默认进程中运行。该名称与应用软件包的名称相同。<application> 元素的 process 属性可为所有组件设置不同的默认进程名称。不过,组件可以使用自己的 process 属性替换默认值,可以将应用散布到多个进程中。
如果为此属性分配的名称以冒号(“:”)开头,则系统会在需要时创建应用专用的新进程,并且服务会在该进程中运行。如果进程名称以小写字符开头,则服务将在使用该名称的全局进程中运行,前提是它拥有相应的权限。如此一来,不同应用中的组件便可共享进程,从而减少资源使用。