Android中startService的使用及Service生命週期
Android中有兩種主要方式使用Service,通過呼叫Context的startService方法或呼叫Context的bindService方法,本文只探討startService的使用,不涉及任何bindService方法呼叫的情況。
當我們通過呼叫了Context的startService方法後,我們便啟動了Service,通過startService方法啟動的Service會一直無限期地執行下去,只有在外部呼叫Context的stopService或Service內部呼叫Service的stopSelf方法時,該Service才會停止執行並銷燬。
要想使用Service,首先我們要繼承自Service,然後重寫如下方法:
onCreate, onStartCommand, onBind 和 onDestroy。
這幾個方法都是回撥方法,都是由Android作業系統在合適的時機呼叫的,並且需要注意的是這幾個回撥方法都是在主執行緒中被呼叫的。
onCreate: 執行startService方法時,如果Service沒有執行的時候會建立該Service並執行Service的onCreate回撥方法;如果Service已經處於執行中,那麼執行startService方法不會執行Service的onCreate方法。也就是說如果多次執行了Context的startService方法啟動Service,Service方法的onCreate方法只會在第一次建立Service的時候呼叫一次,以後均不會再次呼叫。我們可以在onCreate方法中完成一些Service初始化相關的操作。
onStartCommand: 在執行了startService方法之後,有可能會呼叫Service的onCreate方法,在這之後一定會執行Service的onStartCommand回撥方法。也就是說,如果多次執行了Context的startService方法,那麼Service的onStartCommand方法也會相應的多次呼叫。onStartCommand方法很重要,我們在該方法中根據傳入的Intent引數進行實際的操作,比如會在此處建立一個執行緒用於下載資料或播放音樂等。
onBind: Service中的onBind方法是抽象方法,所以Service類本身就是抽象類,也就是onBind方法是必須重寫的,即使我們用不到。在通過startService使用Service時,我們在重寫onBind方法時,只需要將其返回null即可。onBind方法主要是用於給bindService方法呼叫Service時才會使用到。
onDestroy: 通過startService方法啟動的Service會無限期執行,只有當呼叫了Context的stopService或在Service內部呼叫stopSelf方法時,Service才會停止執行並銷燬,在銷燬的時候會執行Service回撥函式。
我們為了探究通過startService方法啟動的Service的生命週期以驗證上面對各個回撥函式方法的描述,寫了如下的一個測試案例。
首先建立一個服務類TestService,該類繼承自Service,程式碼如下:
package com.ispring.startservicedemo; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class TestService extends Service { @Override public void onCreate() { Log.i("DemoLog","TestService -> onCreate, Thread ID: " + Thread.currentThread().getId()); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("DemoLog", "TestService -> onStartCommand, startId: " + startId + ", Thread ID: " + Thread.currentThread().getId()); return START_STICKY; } @Override public IBinder onBind(Intent intent) { Log.i("DemoLog", "TestService -> onBind, Thread ID: " + Thread.currentThread().getId()); return null; } @Override public void onDestroy() { Log.i("DemoLog", "TestService -> onDestroy, Thread ID: " + Thread.currentThread().getId()); super.onDestroy(); } }
我們在TestService的各個回撥方法中只是簡單列印出了相應的資訊,並沒有做很多複雜的處理操作。
然後我們在Activity中呼叫該Serivce,Activity中相應的程式碼如下:
package com.ispring.startservicedemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("DemoLog", "Thread ID: " + Thread.currentThread().getId()); Log.i("DemoLog", "before test startService"); //連續啟動Service Intent intent1 = new Intent(this, TestService.class); startService(intent1); Intent intent2 = new Intent(this, TestService.class); startService(intent2); Intent intent3 = new Intent(this, TestService.class); startService(intent3); //停止Service Intent intent4 = new Intent(this, TestService.class); stopService(intent4); //再次啟動Service Intent intent5 = new Intent(this, TestService.class); startService(intent5); Log.i("DemoLog", "after test startService"); } }
我們在Activity中,首先連續三次呼叫了Activity的startService方法以啟動Service,然後呼叫Activity的stopService方法停止Service,然後又通過呼叫Activity的startService方法啟動Service。
執行程式的輸出結果如下:
我們分析一下上面的輸出結果,首先列印出了主執行緒的ID是1, 然後我們發現後面所有在回撥函式中列印出的執行執行緒的ID也就是1,這就說明了Service中的各個回撥方法是執行在主執行緒中的。其次我們可以發現在我們連續呼叫了三次startService方法之後,只觸發了一次onCreate回撥方法,觸發了三次onStartCommand方法,在onStartCommand中我們可以讀取到通過startService方法傳入的Intent物件,並且這三次的startId都不同,分別是1,2,3,每次呼叫startService都會自動分配一個startId,startId可以用來區分不同的startService的呼叫,一般情況下startId都是從1開始計數,以後每次呼叫startService之後startId自動加一遞增。
之後我們又呼叫了Activity的stopService(intent4)方法用於停止Service,通過輸出結果我們發現Service執行了onDestroy方法,一般情況下我們可以在onDestroy方法中執行一些資源釋放的操作。執行完onDestroy之後該Service的例項就銷燬了。雖然我們之前呼叫了三次startService方法,但是隻要呼叫一次stopService就可以讓執行中的Service停止執行並銷燬。
最後我們再次通過startService(intent5)啟動Service時,通過輸出結果我們發現再次執行了Service的onCreate方法,這說明Service在通過stopService銷燬之後重新建立了,並隨之再次呼叫onStartCommand回撥方法,並且startId再次從1開始計數。
我們用一張圖來概括一下通過startService啟動的Service的生命週期:
當Android面臨記憶體匱乏的時候,可能會銷燬掉你當前執行的Service,然後待記憶體充足的時候可以重新建立Service,Service被Android系統強制銷燬並再次重建的行為依賴於Service中onStartCommand方法的返回值。我們常用的返回值有三種值,START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT,這三個值都是Service中的靜態常量。
START_NOT_STICKY: 如果返回START_NOT_STICKY,表示當Service執行的程式被Android系統強制殺掉之後,不會重新建立該Service,當然如果在其被殺掉之後一段時間又呼叫了startService,那麼該Service又將被例項化。那什麼情境下返回該值比較恰當呢?如果我們某個Service執行的工作被中斷幾次無關緊要或者對Android記憶體緊張的情況下需要被殺掉且不會立即重新建立這種行為也可接受,那麼我們便可將 onStartCommand的返回值設定為START_NOT_STICKY。舉個例子,某個Service需要定時從伺服器獲取最新資料:通過一個定時器每隔指定的N分鐘讓定時器啟動Service去獲取服務端的最新資料。當執行到Service的onStartCommand時,在該方法內再規劃一個N分鐘後的定時器用於再次啟動該Service並開闢一個新的執行緒去執行網路操作。假設Service在從伺服器獲取最新資料的過程中被Android系統強制殺掉,Service不會再重新建立,這也沒關係,因為再過N分鐘定時器就會再次啟動該Service並重新獲取資料。
START_STICKY: 如果返回START_STICKY,表示Service執行的程式被Android系統強制殺掉之後,Android系統會將該Service依然設定為started狀態(即執行狀態),但是不再儲存onStartCommand方法傳入的intent物件,然後Android系統會嘗試再次重新建立該Service,並執行onStartCommand回撥方法,但是onStartCommand回撥方法的Intent引數為null,也就是onStartCommand方法雖然會執行但是獲取不到intent資訊。如果你的Service可以在任意時刻執行或結束都沒什麼問題,而且不需要intent資訊,那麼就可以在onStartCommand方法中返回START_STICKY,比如一個用來播放背景音樂功能的Service就適合返回該值。
START_REDELIVER_INTENT: 如果返回START_REDELIVER_INTENT,表示Service執行的程式被Android系統強制殺掉之後,與返回START_STICKY的情況類似,Android系統會將再次重新建立該Service,並執行onStartCommand回撥方法,但是不同的是,Android系統會再次將Service在被殺掉之前最後一次傳入onStartCommand方法中的Intent再次保留下來並再次傳入到重新建立後的Service的onStartCommand方法中,這樣我們就能讀取到intent引數。只要返回START_REDELIVER_INTENT,那麼onStartCommand重的intent一定不是null。如果我們的Service需要依賴具體的Intent才能執行(需要從Intent中讀取相關資料資訊等),並且在強制銷燬後有必要重新建立執行,那麼這樣的Service就適合返回START_REDELIVER_INTENT。
相關文章
- Android中bindService的使用及Service生命週期Android
- Android Service生命週期淺析Android
- JavaScript 是如何工作的:Service Worker 的生命週期及使用場景JavaScript
- Service生命週期詳解
- Android Service生命週期 Service裡面的onStartCommand()方法詳解Android
- Android:聊聊 MVP 中 Presenter 的生命週期AndroidMVP
- [Android]Activity的生命週期Android
- Android MediaPlayer的生命週期Android
- android:碎片的生命週期Android
- Android Activity生命週期Android
- Flutter 中的生命週期分析Flutter
- Java 中物件的生命週期Java物件
- Android之各生命週期Android
- Android Activit生命週期方法Android
- [譯] JavaScript 是如何工作的:Service Worker 的生命週期與使用場景JavaScript
- Vue例項及生命週期Vue
- Activity簡介及生命週期
- Android View的生命週期詳解AndroidView
- Android生命週期元件Lifecycle使用詳解Android元件
- Service的啟動、繫結,以及生命週期的筆記筆記
- laravel中的$request物件構造及請求生命週期Laravel物件
- Spring中bean的生命週期SpringBean
- (譯)理解Rust中的生命週期Rust
- View生命週期與Activity生命週期的關係View
- Android Fragment生命週期深入探究AndroidFragment
- Android 元件系列-----Activity生命週期Android元件
- Android Activity生命週期詳解Android
- Android程式生命週期ProcessLifecycleAndroid
- 生命週期
- Android Activity生命週期的一點感悟Android
- 初識Android之Activity的生命週期Android
- Android Fragment的生命週期和返回棧AndroidFragment
- React元件的狀態及生命週期事件React元件事件
- Android學習路線_入門篇(五)Service的生命週期與如何保活Android
- Flutter 的生命週期Flutter
- SQL的生命週期SQL
- Laravel的生命週期Laravel
- vue的生命週期Vue