Service的onStartCommand几种返回值的用法

Android开发者都知道Service的onStartCommand方法,其返回值会决定Service的不同特性,比如Service被杀死后能否可以自动重启。本文详细说一下各返回值的作用。安卓源码里面给出了四种返回值,分别是START_STICKY、START_NOT_STICKY、STRAT_REDELIVER_INTENT、START_STICKY_COMPATIBILITY。

/**
 * Constant to return from {@link #onStartCommand}: compatibility
 * version of {@link #START_STICKY} that does not guarantee that
 * {@link #onStartCommand} will be called again after being killed.
 */
public static final int START_STICKY_COMPATIBILITY = 0;


这个常量是STRAT_STICKY的通用版本,不保证服务被杀死之后,onStartCommand方法会被重新调用。

/**
 * Constant to return from {@link #onStartCommand}: if this service's
 * process is killed while it is started (after returning from
 * {@link #onStartCommand}), then leave it in the started state but
 * don't retain this delivered intent.  Later the system will try to
 * re-create the service.  Because it is in the started state, it will
 * guarantee to call {@link #onStartCommand} after creating the new
 * service instance; if there are not any pending start commands to be
 * delivered to the service, it will be called with a null intent
 * object, so you must take care to check for this.
 * 
 * <p>This mode makes sense for things that will be explicitly started
 * and stopped to run for arbitrary periods of time, such as a service
 * performing background music playback.
 */
public static final int START_STICKY = 1;
 

该常量的作用:如果Service在启动之后(在运行onStartCommand之后)被杀死了,那么保持服务为started状态,但是不会重新获取这个投递的intent。之后系统会尝试重新create这个服务。因为服务处于started状态,会保证在创造新的service实例之后会调用onStartCommand方法。如果没有等待的启动命令要投递给服务,那么传进来额intent对象就是null,所以要检查intent是否为null。该模式使用场景是在任意时间显示地启动和停止的工作,比如执行后台音乐重放的服务。(补充:onStartCommand方法第一个形参是Intent,这个对象是怎么来的呢,使我们在调用startService方法传进来的Intent对象)

/**
 * Constant to return from {@link #onStartCommand}: if this service's
 * process is killed while it is started (after returning from
 * {@link #onStartCommand}), and there are no new start intents to
 * deliver to it, then take the service out of the started state and
 * don't recreate until a future explicit call to
 * {@link Context#startService Context.startService(Intent)}.  The
 * service will not receive a {@link #onStartCommand(Intent, int, int)}
 * call with a null Intent because it will not be re-started if there
 * are no pending Intents to deliver.
 * 
 * <p>This mode makes sense for things that want to do some work as a
 * result of being started, but can be stopped when under memory pressure
 * and will explicit start themselves again later to do more work.  An
 * example of such a service would be one that polls for data from
 * a server: it could schedule an alarm to poll every N minutes by having
 * the alarm start its service.  When its {@link #onStartCommand} is
 * called from the alarm, it schedules a new alarm for N minutes later,
 * and spawns a thread to do its networking.  If its process is killed
 * while doing that check, the service will not be restarted until the
 * alarm goes off.
 */
public static final int START_NOT_STICKY = 2;
 

该常量的作用:如果服务进程在启动之后(执行完onStartCommand方法之后)被杀死了,并且没有新的启动intent,那么将会把这个服务脱离started状态并且不会recreate直到一个显示的调用startService到来。这个service不会受到带有null intent对象的onStartCommand的调用,如果没有等待的Intents,它不会被系统重启。

这个模式的使用场景:做一些工作由于被启动,但是能够被停止当内存不够的时候,并且将会在之后显示启动自己来做更多的工作。一个例子就是从服务器拉取数据:安排一个alarm,这个alarm每隔n分钟来启动服务获取数据,当服务的onStartCommand方法被alarm调用的时候,它将会安排一个新的alarm设定N分钟之后触发,并 开启一个线程来做网络请求。如果在做检查的时候进程被杀死了,这个服务知道alarm触发才会被重新创建。

/**
 * Constant to return from {@link #onStartCommand}: if this service's
 * process is killed while it is started (after returning from
 * {@link #onStartCommand}), then it will be scheduled for a restart
 * and the last delivered Intent re-delivered to it again via
 * {@link #onStartCommand}.  This Intent will remain scheduled for
 * redelivery until the service calls {@link #stopSelf(int)} with the
 * start ID provided to {@link #onStartCommand}.  The
 * service will not receive a {@link #onStartCommand(Intent, int, int)}
 * call with a null Intent because it will will only be re-started if
 * it is not finished processing all Intents sent to it (and any such
 * pending events will be delivered at the point of restart).
 */
public static final int START_REDELIVER_INTENT = 3;
 

该常量的作用:如果服务进程在启动之后(执行完onStartCommand方法之后)被杀死了,那么它将会安排重启,并且最近的intent会重新投递给onStartCommand方法。这个intent将会保持被重新投递的安排,直到服务调用stopSelf方法,参数startID和onStartCommand方法里面的startID一样。这个服务不会接受一个intent对象为null的onStartCommand方法,因为它只有在没有处理完所有intents对象的时候才会被重启。

 

根据源码的注释可以知道,只有START_STICKY和START_REDELIVER_INTENT常量才会是服务被杀死后有系统重启,那么系统能否保证重启service,在何种条件下才会重启service,什么时候不会重启service呢。我们先来测试一下START_STICKY常量,服务被杀的方法进行两种:1、调用stopService自杀;2、手动强杀进程。下面是测试结果(我的测试设备是android 7.0的)。

1.主动调用stopService,startService在activity的onCreate里面调用,stopService通过一个按钮的click事件来触发。运行日志如下:

08-02 17:39:58.545 6384-6384 D/MyService: onCreate
08-02 17:39:58.547 6384-6384 D/MyService: onStartCommand
08-02 17:40:01.644 6384-6384 D/MyService: onclick
08-02 17:40:01.650 6384-6384 D/MyService: onDestroy
08-02 17:41:18.160 6384-6384 D/MyService: onCreate
08-02 17:41:26.161 6384-6384 D/MyService: onDestroy

这里有个问题就是service在destroy之后,的确被系统recreate了,但是之后没有走onStartCommand,而是直接走到onDestroy,之后再也没有recreate。有兴趣的同学可以从源码方面深入研究一下service的生命周期管理流程。

2、手动强杀进程,我们在服务启动之后,手动强杀进程,运行日志如下:

08-02 20:04:36.365 8580-8580 D/MyService: onCreate
08-02 20:04:36.365 8580-8580 D/MyService: onStartCommand
08-02 20:04:36.367 8580-8580 D/MyService: intent = Intent { cmp=*****.MyService }
08-02 20:04:50.974 8638-8638 D/MyService: onCreate
08-02 20:04:50.977 8638-8638 D/MyService: onStartCommand
08-02 20:04:50.979 8638-8638 D/MyService: intent = null

从日志上看,返回START_STICKY对于手动强杀效果还是很正常的。

下面我们再看一下返回START_REDELIVER_INTENT的测试结果。

1、主动调用stopService,测试结果如下:

08-02 20:15:06.212 8810-8810 D/MyService: onCreate
08-02 20:15:06.213 8810-8810 D/MyService: onStartCommand
08-02 20:15:09.018 8810-8810 D/MyService: onclick
08-02 20:15:09.022 8810-8810 D/MyService: onDestroy
08-02 20:17:17.222 8810-8810 D/MyService: onCreate
08-02 20:17:25.226 8810-8810 D/MyService: onDestroy

从日志来看,该过程和返回START_STICKY的测试过程1的结果相同。

2、手动强杀进程,测试结果如下:

08-02 20:26:25.647 9092-9092 D/MyService: onCreate
08-02 20:26:25.647 9092-9092 D/MyService: onStartCommand
08-02 20:26:25.648 9092-9092 D/MyService: intent = Intent { cmp=***.MyService }
08-02 20:26:44.246 9144-9144 D/MyService: onCreate
08-02 20:26:44.249 9144-9144 D/MyService: onStartCommand
08-02 20:26:44.256 9144-9144 D/MyService: intent = Intent { cmp=***.MyService }

从日志上看,可以看出这个过程和返回START_STICKY的测试过程1的结果大致流程是一样的,不同的是重启之后onStartCommand的Intent对象是不同的,START_STICKY为null,START_REDELIVER_INTENT则不为null,这个和源码里面的注释说明是相一致的。

看到这里,我们似乎看到了一个进程/service保活的方法,只要在onStartCommand方法的返回值设为START_STICKY或者START_REDELIVER_INTENT就OK了,别高兴太早,在原生安卓机上这个方法没问题,但是我国的安卓设备厂商会各种修改源码的,特别为了提高系统安全性而作各种修改。所以要实现进程/service防杀,单单用上述方法只能适用一部分机型。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android海纳百川

打赏加微信,送跑车加管理

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值