1. 程式人生 > >Service 中 onStartCommand 回撥四種返回值的區別

Service 中 onStartCommand 回撥四種返回值的區別

服務的兩種建立方式

Service 是一個可以在後臺執行長時間執行操作而不提供使用者介面的應用元件。服務可由其他應用元件啟動,而且即使使用者切換到其他應用,服務仍將在後臺繼續執行。此外,元件可以繫結到服務,以與之進行互動,甚至是執行程序間通訊 (IPC)。例如,服務可以處理網路事務、播放音樂,執行檔案 I/O 或與內容提供程式互動,而所有這一切均可在後臺進行。

大家都知道服務的啟動方式基本有以下兩種:

1、啟動

當應用元件(如 Activity)通過呼叫 startService() 啟動服務時,服務即處於”啟動”狀態。一旦啟動,服務即可在後臺無限期執行,即使啟動服務的元件已被銷燬也不受影響。已啟動的服務通常是執行單一操作,而且不會將結果返回給呼叫方。例如,它可能通過網路下載或上傳檔案。操作完成後,服務會自行停止執行。

2、繫結

當應用元件通過呼叫 bindService() 繫結到服務時,服務即處於”繫結”狀態。繫結服務提供了一個客戶端–伺服器介面,允許元件與服務進行互動、傳送請求、獲取結果,甚至是利用程序間通訊 (IPC) 跨程序執行這些操作。僅當與另一個應用元件繫結時,繫結服務才會執行。多個元件可以同時繫結到該服務,但全部取消繫結後,該服務即會被銷燬。

兩種方式建立的服務生命週期如下圖,本文的主題便是跟第一種執行方式有關 service_lifecycle

注意:服務在其託管程序的主執行緒中執行,它既不建立自己的執行緒,也不在單獨的程序中執行(除非另行指定)。
這意味著,如果服務將執行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或聯網),則應在服務內建立新執行緒來完成這項工作。
通過使用單獨的執行緒,可以降低發生"ANR"
錯誤的風險,而應用的主執行緒仍可繼續專注於執行使用者與 Activity 之間的互動。

當另一個元件(如 Activity)通過呼叫 startService() 請求啟動服務時,系統將呼叫此方法。一旦執行此方法,服務即會啟動並可在後臺無限期執行。如果你實現此方法,則在服務工作完成後,需要由你通過呼叫 stopSelf()stopService() 來停止服務。(如果你只想提供繫結,則無需實現此方法。) 此方法返回型別為 int 型,用於描述系統應該如何在服務終止的情況下繼續執行服務,返回值必須是以下常量之一:

作為 Service.START_STICKY 的相容版本,不能保證服務被 kill 後再次執行

onStartCommand() 回撥。

如果系統在 onStartCommand() 返回後終止服務,則會重建服務並呼叫 onStartCommand(),但不會重新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啟動服務(在這種情況下,將傳遞這些 Intent),否則系統會通過空 Intent 呼叫 onStartCommand()。這適用於不執行命令、但無限期執行並等待作業的媒體播放器(或類似服務)。

如果系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務。這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時執行服務。

如果系統在 onStartCommand() 返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 呼叫 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載檔案)的服務。

程式碼演示

服務類

public class MyService extends Service {

    private static final String TAG = MyService.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate");
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "postDelayed");
                // 製造異常,kill 掉該 Service
                int a = 1 / 0;
            }
        }, 3000L);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand, intent: " + intent + ", startId: " + startId);
        return Service.START_STICKY_COMPATIBILITY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
    }
}

測試類

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(MainActivity.this, MyService.class));
    }
}

測試結果

1、使用 START_STICKY_COMPATIBILITY 作為返回值

03-22 20:41:55.619 9311-9311/com.example.test E/MyService: onCreate
03-22 20:41:55.619 9311-9311/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:41:58.619 9311-9311/com.example.test E/MyService: postDelayed
// 服務重新建立,但是沒有重啟
03-22 20:42:05.079 9489-9489/com.example.test E/MyService: onCreate
03-22 20:42:08.079 9489-9489/com.example.test E/MyService: postDelayed

2、使用 START_STICKY 作為返回值

03-22 20:43:33.219 11068-11068/com.example.test E/MyService: onCreate
03-22 20:43:33.219 11068-11068/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:43:36.229 11068-11068/com.example.test E/MyService: postDelayed
// 服務重新建立並重啟,但是 intent 物件被置空了
03-22 20:43:42.199 11233-11233/com.example.test E/MyService: onCreate
03-22 20:43:42.209 11233-11233/com.example.test E/MyService: onStartCommand, intent: null, startId: 2
03-22 20:43:45.209 11233-11233/com.example.test E/MyService: postDelayed

3、使用 START_NOT_STICKY 作為返回值

03-22 20:44:49.459 12437-12437/com.example.test E/MyService: onCreate
03-22 20:44:49.469 12437-12437/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:44:52.469 12437-12437/com.example.test E/MyService: postDelayed
// 服務沒有重新建立

4、使用 START_REDELIVER_INTENT 作為返回值

03-22 20:45:49.969 13525-13525/com.example.test E/MyService: onCreate
03-22 20:45:49.969 13525-13525/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:45:52.969 13525-13525/com.example.test E/MyService: postDelayed
// 服務重新建立並啟動,intent 物件沒有被置空
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onCreate
03-22 20:46:00.879 14792-14792/com.example.test E/MyService: onStartCommand, intent: Intent { cmp=com.example.test/.MyService }, startId: 1
03-22 20:46:03.889 14792-14792/com.example.test E/MyService: postDelayed