此时情绪此时天,无事小神仙
好好生活,平平淡淡每一天

编辑

Android Service的onStartCommand方法的返回值和参数详解

前言:

Android Service的onStartCommand方法是在通过startService()的方式启动Service的时候被调用的生命周期方法。

需要注意的是,如果多次用startService()尝试启动同一个Service,该Service的onStartCommand方法就会多次被调用。但是该Service的onCreate方法只会被调用一次,只会创建一个Service实例。

下面重点介绍一下onStartCommand方法的返回值和参数。看下Android SDK源码里Service类的onStartCommand方法的定义:

/**
 * @param intent The Intent supplied to {@link android.content.Context#startService}, 
 * as given.  This may be null if the service is being restarted after
 * its process has gone away, and it had previously returned anything
 * except {@link #START_STICKY_COMPATIBILITY}.
 * @param flags Additional data about this start request.  Currently either
 * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.
 * @param startId A unique integer representing this specific request to 
 * start.  Use with {@link #stopSelfResult(int)}.
 * 
 * @return The return value indicates what semantics the system should
 * use for the service's current started state.  It may be one of the
 * constants associated with the {@link #START_CONTINUATION_MASK} bits.
 * 
 * @see #stopSelfResult(int)
 */
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}

上面的注释,只截取了对参数和返回值的注释部分。下面先对返回值进行分析:

onStartCommand的返回值

根据源码,默认情况下,要么返回START_STICKY_COMPATIBILITY ,要么返回START_STICKY。但我们在重写ononStartCommand方法的时候,还有另外两种返回值START_NOT_STICKY 和 START_REDELIVER_INTENT可选。

这四种返回值分别是什么意思呢?通过源码注释一一比较。

1. START_STICKY

/**
 * 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;

上面的注释也就是说,在onStartCommand方法返回START_STICKY 的情况下:
如果Service所在的进程,在执行了onStartCommand方法后,被清理了,那么这个Service会被保留在已开始的状态,但是不保留传入的Intent,随后系统会尝试重新创建此Service,由于服务状态保留在已开始状态,所以创建服务后一定会调用onStartCommand方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null,需要我们小心处理。

2.START_NOT_STICKY

/**
 * 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方法返回START_NOT_STICKY 的情况下:
如果Service所在的进程,在执行了onStartCommand方法后,被清理了,则系统不会重新启动此Service。

3.START_REDELIVER_INTENT

/**
 * 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方法返回START_REDELIVER_INTENT 的情况下:
如果Service所在的进程,在执行了onStartCommand方法后,被清理了,则结果和START_STICKY一样,也会重新创建此Service并调用onStartCommand方法。不同之处在于,如果是返回的是START_REDELIVER_INTENT ,则重新创建Service时onStartCommand方法会传入之前的intent。 **(从名字上就可以理解,REDELIVER INTENT,重新提交intent)

4.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;

这个比较简单,是START_STICKY的兼容版本,但是不能保证被清理后onStartCommand方法一定会被重新调用。

好了,接下来看onStartCommand的参数。

onStartCommand的参数

有三个参数,分别是intent对象,int类型的flags和startId。

1. intent:

不用多说了,就是startService时传入的intent。重点看下flags和startId:

2. flags:

根据源码注释,flags有三个可以传入的值:0,START_FLAG_REDELIVERY和START_FLAG_RETRY。

0:
在正常创建Service的情况下,onStartCommand传入的flags为0。

START_FLAG_REDELIVERY:

/**
 * This flag is set in {@link #onStartCommand} if the Intent is a
 * re-delivery of a previously delivered intent, because the service
 * had previously returned {@link #START_REDELIVER_INTENT} but had been
 * killed before calling {@link #stopSelf(int)} for that Intent.
 */
public static final int START_FLAG_REDELIVERY = 0x0001;

根据以上注释,也就是说如果onStartCommand返回的是START_REDELIVER_INTENT,并且Service被系统清理掉了,那么重新创建Service,调用onStartCommand的时候,传入的intent不为null,而传入的flags就是START_FLAG_REDELIVERY 。

START_FLAG_RETRY:

/**
 * This flag is set in {@link #onStartCommand} if the Intent is a
 * retry because the original attempt never got to or returned from
 * {@link #onStartCommand(Intent, int, int)}.
 */
public static final int START_FLAG_RETRY = 0x0002;

根据以上注释,也就是说如果Service创建过程中,onStartCommand方法未被调用或者没有正常返回的异常情况下, 再次尝试创建,传入的flags就为START_FLAG_RETRY 。

3. startId:

 * @param startId A unique integer representing this specific request to 
 * start.  Use with {@link #stopSelfResult(int)}.

就是说,传入的这个startId 用来代表这个唯一的启动请求。我们可以在stopSelfResult(int startId)中传入这个startId,用来终止Service。

那么stopSelfResult(int startId)和通过stopService方法来终止Service有何不同呢?看源码stopSelfResult方法的注释:

* Stop the service if the most recent time it was started was 
* <var>startId</var>.  This is the same as calling {@link 
* android.content.Context#stopService} for this particular service but allows you to 
* safely avoid stopping if there is a start request from a client that you 
* haven't yet seen in {@link #onStart}.

就是说,stopSelfResult方法传入的startId,必须是最后一次启动Service时传入的startId,才能终止Service。万一我们想终止Service的时候又来了个启动请求,这时候是不应该终止的,而我们还没拿到最新请求的startId,如果用stopService的话就直接终止了,而用stopSelfResult方法就会及时避免终止。

posted @ 2019-12-30 14:34  踏步  阅读(637)  评论(0编辑  收藏  举报