Android中有兩種主要方式使用Service,通過調用Context的startService方法或調用Context的bindService方法。本文僅僅探討純startService的使用。不涉及不論什么bindService方法調用的情況。假設想了解bindService的相關使用,請參見《Android中bindService的使用及Service生命周期》。
當我們通過調用了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開始計數。
最后須要注意的是我們在Activity中操作Service的開始和結尾處分別寫了兩句輸出代碼,各自是
Log.i("DemoLog", "before test startService");
和
Log.i("DemoLog", "after test startService");
可是我們再看一下輸出結果會發現。程序直接上來在輸出了before test startService之后。卻馬上輸出了after test startService,在這之后才是TestService內部各個回調方法的輸出。這說明startService()方法和stopService()方法在運行完后馬上返回了,也就是這兩個方法都不是堵塞式的,啟動service和停止service都是異步操作,startService()、stopService()都是將intent對象發送給Android Framework,然后Framework層異步地啟動、停止Service。
我們用一張圖來概括一下通過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須要定時從server獲取最新數據:通過一個定時器每隔指定的N分鍾讓定時器啟動Service去獲取服務端的最新數據。當運行到Service的onStartCommand時,在該方法內再規划一個N分鍾后的定時器用於再次啟動該Service並開辟一個新的線程去運行網絡操作。假設Service在從server獲取最新數據的過程中被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通過startService播放背景音樂簡單演示樣例
Android通過startService實現批量下載演示樣例