前言:
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方法就會及時避免終止。