Android Service onStartCommand 理解

onStartCommand不同的返回值对Service有什么影响?

如何去验证不同返回值对Service的影响?

这里写一些个人理解,如果存在错误的地方,希望您给予指点和评论。

首先肯定是找到Service官方文档,这里的解释肯定比一些书籍或者博客的解释更为权威和全面。

先插一段话,官方提供的中文版翻译有些地方难以理解,甚至会让人产生误导,所以下面我翻译的内容有些地方会和官方不大一样。

下面看看官方的解释

  • START_NOT_STICKY

如果系统在 onStartCommand()返回后终止服务,则除有待传递的intent,否则系统不会重建服务。这是最安全的选项,以避免在不必要时运行服务,以及应用程序可以简单地重新启动任何未完成的作业。

在API级别5中添加 int START_NOT_STICKY
从onStartCommand(Intent,int,int)返回的常量:如果此服务的进程在启动时被杀死(从onStartCommand(Intent,int,int)返回后),并且没有新的启动意向传递给它,使服务脱离启动状态,并且不重新创建,直到未来显式调用Context.startService(Intent)。该服务将不会收到一个带有Intent的onStartCommand(Intent,int,int)调用,因为如果没有待传递的意图,它不会被重新启动。

这种模式对于想要作为启动的结果做一些工作的东西是有意义的,但是可以在内存压力下停止,并且将在稍后显式地重新启动自己来做更多的工作。这样的服务的示例将是从服务器轮询数据的一个示例:它可以通过使报警开始其服务来调度报警以每N分钟轮询。当它的onStartCommand(Intent,int,int)从报警中调用时,它会在N分钟后安排一个新的报警,并产生一个线程来做它的网络。如果在执行此检查时,其进程被终止,则在警报关闭之前,服务将不会重新启动。

个人理解:如果返回此属性,当Service被kill,系统不会重建服务,如果你需要再次启动服务,可使用startService(Intent)重新开启服务来请求Service完成该Intent传递过来的任务。
  • START_STICKY

    如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent(but do not redeliver the last intent)。相反,除非有 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

START_STICKY

在API级别5中添加 int START_STICKY
从onStartCommand(Intent,int,int)返回的常量:如果这个服务的进程在启动时被杀死(从onStartCommand(Intent,int,int)返回后),然后将其保留在启动状态,交付意图。稍后系统将尝试重新创建服务。因为它是在启动状态,它将保证在创建新的服务实例后调用onStartCommand(Intent,int,int);如果没有任何待发送到服务的启动命令,它将使用一个null意图对象来调用,因此您必须小心检查这一点。

此模式对于明确启动和停止运行任意时间段(例如执行背景音乐播放的服务)的事物有意义。

个人理解
如果返回此属性,当Service被kill,系统会重建服务,并传递一个空的Intent来调用onStartCommand(),所以设置此属性一定要做判空处理。

上面有一段话不好理解“但不会重新传递最后一个 Intent”,什么叫做最后一个Intent?看完下面这个属性的解释,你就会知道最后一个Intent的意思了,这也是该属性和START_REDELIVER_INTENT的差异所在。
  • START_REDELIVER_INTENT

如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用
onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

START_REDELIVER_INTENT

在API级别5中添加 int START_REDELIVER_INTENT
从onStartCommand(Intent,int,int)返回的常量:如果此服务的进程在启动时被杀死(从onStartCommand(Intent,int,int)返回后),那么它将被安排重新启动,通过onStartCommand(Intent,int,int)重新传递给它。此Intent将保持计划重新传递,直到服务调用带有提供给onStartCommand(Intent,int,int)的开始ID的stopSelf(int)。该服务将不会接收到一个带有Intent的onStartCommand(Intent,int,int)调用,因为它只有在没有完成处理发送给它的所有Intents时才会重新启动(并且任何这样的挂起事件将在重启点)。

个人理解:如果返回此属性,当Service被kill,如果之前传递的多个Intent任务中有任务没有处理完成,系统会重建服务,并紧接着之前未完成的Intent任务,按照之前传递的顺序依次执行。

该属性和上面START_STICKY属性区别在于,终止服务后,系统重建时,如果onStartCommand() 返回值为START_STICKY,这时重建服务onStartCommand()传递过来Intent为null,但是onStartCommand() 返回值为START_REDELIVER_INTENT时,Intent不是null,那Intent是什么呢?知道这个Intent就能理解“最后一个Intent”的意思了。
下面我用模拟器模拟了这个行为,我的操作是这样的:
页面有五个Button,分别用来向Service发送请求,对位事件为,下载文件1,下载文件2,下载文件3,下载文件4,下载文件5。这个Service类似IntentService,按队列顺序来执行每一个任务,假设每个文件下载耗时10秒吧。现在很快速的点击这五个按钮,这时候五个Intent都发送给Service了,这些任务在工作线程里依次执行,当文件正在下载中的时候,我按Home键,并在最近运行的应用中关闭了这个App。这时候Service终止,下载任务也停止。稍等片刻,Service重建了,重新执行了onCreate和onStartCommand方法,重点是这时候onStartCommand传递过来的是文件2的Intent。也就是说重建后,Service接着之前没下载完的文件,依次去下载了,依次下载文件2,文件3,文件4,文件5。这下你可以理解官方文档中这样一句话了“但不会重新传递最后一个 Intent(but do not redeliver the last intent)”,其实就是用来区分START_STICKY和START_REDELIVER_INTENT的。

以上结论是通过手动终止服务的方式来验证的,使用的是模拟器设备,关于系统因为内存不足终止服务的行为,个人不知如何验证,待解答。

onStartCommand参数

onStartCommand方法有三个参数
onStartCommand(Intent intent, int flags, int startId)

  • intent

    向Service请求任务时候传递的intent,intent中可以传递一些任务信息,比如说传递一个下载地址给Service,在Service中开启一个线程去下载。

  • flags

    flags有三个值,分别是0,START_FLAG_REDELIVERY,START_FLAG_RETRY。
    当调用startService来启动服务的时候,这里的值是0,如果Service发生重启行为,flags就不会是0了(比如你的onStartCommand返回值是START_STICKY 或 START_REDELIVER_INTENT,则Service终止后会重新启动)。他们的区别在于,如过Service重新启动了,当Service在onStartCommand返回之前就被终止了,那么下次重启时flags的值为START_FLAG_RETRY,当Service在onStartCommand返回后并且在stopSelf(int)还没执行前被终止,那么下次启动时flags的值为START_FLAG_REDELIVERY。

  • startId
    每次调用startService向Service传递任务请求时都会生成一个startId,用来代表这一次请求,这个值是唯一的,不管startService调用多少次,每一次的startId的值都是不一样的,这个值和stopSelfResult(int)或者stopSelf(int)一起使用,主要是为了确保Service中的所有任务都已经完成。

可以这样理解,如果一次向Service请求5次,如何能才能保证5次任务都执行完了,才会停止服务呢。可以这样,我们知道会执行5次,定义一个变量,累加,当等于5的时候调用stopSelf(),但是如果不知道请求了多少次,要在最后一次任务执行完后终止服务,这个办法就行不通了。官方提供的stopSelfResult(int)就是用来解决这个问题的,参数传入onStartCommand参数中的startId。Service内部记录了最后一次启动Service的标示startId,当调用stopSelfResult时传入的startId,如果传入的startId,和Service内部记录的最后一次startId不一致,则不会终止服务。

以上内容验证基于 ApiDemos中的ServiceStartArguments.java。

你可能感兴趣的:(基础)