Android开发Android开发经验谈

Service详解_初识及启动服务实现

2019-01-09  本文已影响1人  一笑小先生

Service内容基本会涉及到,我们将围绕以下主要知识点进行分析:

1. Service简单概述

Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。用户无法感知它的存在。Service可由其他组件启动(如Activity),服务一旦被启动,将在后台一直运行,即使服务的组件已经销毁也不受到影响。Service组件和Activity组件略有不同,Activity组件只有一种运行模式,即Activity处于启动状态,但是Service有两种状态:

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>

3. Service生命周期

官方说明图:


service_lifecycle.png

Service生命周期方法:

其中onStartCommand(Intent intent, int flags, int startId)方法比较重要,这个方法有3个传入参数,它们的含义如下:

START_FLAG_REDELIVERY
这个值代表了onStartCommand方法的返回值为 START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。其中START_REDELIVER_INTENT意味着当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的。
START_FLAG_RETRY
该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。

实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值:

  1. START_STICKY
      当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。
  2. START_NOT_STICKY
      当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
  3. START_REDELIVER_INTENT
      当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
    由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制

4. Service启动服务实现方式

4.1 使用步骤

需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法

下面我们通过简单案例来实现并分析:

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返回值,以达到对程序更友好的控制。

上一篇 下一篇

猜你喜欢

热点阅读