Service内容基本会涉及到,我们将围绕以下主要知识点进行分析:
- Service简单概述
- Service在清单文件中的声明
- Service生命周期
- Service启动服务实现方式
1. Service简单概述
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。用户无法感知它的存在。Service可由其他组件启动(如Activity),服务一旦被启动,将在后台一直运行,即使服务的组件已经销毁也不受到影响。Service组件和Activity组件略有不同,Activity组件只有一种运行模式,即Activity处于启动状态,但是Service有两种状态:
启动状态
当应用组件(如Activity)通过调用startService()启动服务时,服务即处于启动状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已经销毁也不受到影响,除非手动调用才能停止,已启动的服务通常是执行单一操作,不会将结果返回给调用方。绑定状态
当应用组件通过调用bindService()绑定到服务时,服务即处于“绑定”状态。绑定服务提供一个客户端-服务端接口,允许组件与服务进行交互、发送请求、获取结果,甚至是进行进程间通信(IPC)跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可以同时绑定该服务,但全部取消绑定后,服务即被销毁。
2. Service在清单文件中的声明
前面说过Service分为启动状态和绑定状态两种,但无论哪种具体的Service启动类型,都是通过继承Service基类自定义而来,也都需要在AndroidManifest.xml中声明,那么在分析这两种状态之前,我们先来了解一下Service在AndroidManifest.xml中的声明语法,其格式如下:
<service 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:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
- android:name:对应Service类名
- android:permission:是权限声明
- android:process:是否需要在单独的进程中运行,当设置为- android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
- android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
- android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。
3. Service生命周期
官方说明图:
Service生命周期方法:
- onBind() 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。
- onCreate() 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次。
- onStartCommand(Intent intent, int flags, int startId) 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法)
- onDestroy() 当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
其中onStartCommand(Intent intent, int flags, int startId)方法比较重要,这个方法有3个传入参数,它们的含义如下:
-
intent :启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service
flags:表示启动请求时是否有额外数据,可选值有 0,START_FLAG_REDELIVERY
,START_FLAG_RETRY
,0代表没有,它们具体含义如下:
START_FLAG_REDELIVERY
这个值代表了onStartCommand方法的返回值为START_REDELIVER_INTENT
,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT
意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的。
START_FLAG_RETRY
该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
- startId: 指明当前服务的唯一ID,与stopSelfResult (int startId)配合使用,stopSelfResult 可以更安全地根据ID停止服务。
实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值:
-
START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。 -
START_NOT_STICKY
当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。 -
START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY
不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制
4. Service启动服务实现方式
4.1 使用步骤
- 步骤1:新建子类继承Service类
需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法
- 步骤2:构建用于启动Service的Intent对象
- 步骤3:调用startService()启动Service、调用stopService()停止服务
- 步骤4:在AndroidManifest.xml里注册Service
下面我们通过简单案例来实现并分析:
class SimpleService : Service() {
/**
* 绑定服务(bindService)时调用
*/
override fun onBind(intent: Intent?): IBinder? = null
/**
* 首次创建服务时调用,系统调用此方法一次性预设程序(在调用 onStartCommand() 或 onBind() 之前)
* 如果服务已在运行,则不会调用此方法。该方法只被调用一次
*/
override fun onCreate() {
super.onCreate()
System.out.println("OnCreate invoke")
}
/**
* 每次通过startService()方法启动Service时都会被调用
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
System.out.println("onStartCommand invoke")
return super.onStartCommand(intent, flags, startId)
}
/**
* 服务销毁时的调用
*/
override fun onDestroy() {
super.onDestroy()
System.out.println("onDestroy invoke")
}
}
从上面的代码我们可以看出SimpleService继承了Service类,并重写了onBind方法,该方法是必须重写的,但是由于此时是启动状态的服务,则该方法无须实现,返回null即可,只有在绑定状态的情况下才需要实现该方法并返回一个IBinder的实现类。接着重写了onCreate、onStartCommand、onDestroy三个主要的生命周期方法,关于方法的介绍,上面👆已经阐述。
我们通过Demo测试一下Service启动状态方法的调用顺序,MainActivity代码如下:
class MainActivity : AppCompatActivity() {
private var simpleIntent: Intent? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startServiceBtn.setOnClickListener {
simpleIntent = Intent(this, SimpleService::class.java)
startService(simpleIntent)
}
stopServiceBtn.setOnClickListener {
simpleIntent?.let { intent ->
stopService(intent)
}
}
}
}
记得在清单配置文件中声明Service(声明方式跟Activity相似):
<manifest ... >
...
<application ... >
<service android:name=".SimpleService" />
...
</application>
</manifest>
从代码看出,启动服务使用startService(Intent intent)方法,仅需要传递一个Intent对象即可,在Intent对象中指定需要启动的服务。而使用startService()方法启动的服务,在服务的外部,必须使用stopService()方法停止,在服务的内部可以调用stopSelf()方法停止当前服务。如果使用startService()或者stopSelf()方法请求停止服务,系统会就会尽快销毁这个服务。值得注意的是对于启动服务,一旦启动将与访问它的组件无任何关联,即使访问它的组件被销毁了,这个服务也一直运行下去,直到手动调用停止服务才被销毁,至于onBind方法,只有在绑定服务时才会起作用,在启动状态下,无需关注此方法,ok~,我们运行程序并多次调用startService方法,最后调用stopService方法。Log输出如下:
01-08 09:28:37.344 23543-23543/com.wangyy.service I/System.out: OnCreate invoke
01-08 09:28:37.344 23543-23543/com.wangyy.service I/System.out: onStartCommand invoke
01-08 09:28:48.950 23543-23543/com.wangyy.service I/System.out: onStartCommand invoke
01-08 09:28:49.171 23543-23543/com.wangyy.service I/System.out: onStartCommand invoke
01-08 09:28:49.317 23543-23543/com.wangyy.service I/System.out: onStartCommand invoke
01-08 09:28:49.496 23543-23543/com.wangyy.service I/System.out: onStartCommand invoke
01-08 09:28:51.089 23543-23543/com.wangyy.service I/System.out: onDestroy invoke
从Log可以看出,第一次调用startService方法时,onCreate方法、onStartCommand方法将依次被调用,而多次调用startService时,只有onStartCommand方法被调用,最后我们调用stopService方法停止服务时onDestory方法被回调,这就是启动状态下Service的执行周期。
由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制。