本節講述Android應用程序組件中的Service。所謂Service就是在後臺執行且沒有提供用戶界面的組件,即使用戶切換到其他應用程序,service仍然在後臺運行不受影響。通常service被用來在後臺執行耗時的操作,比如網絡通信、播放音樂、執行文件I/O、與content provider交互等。
Service有兩種類型,分爲啓動(started)和綁定(bound)兩種。啓動service由startService()完成,一旦service啓動,它能夠一直在後臺運行即使啓動它的組件已經被銷燬,通常被用來執行單獨的操作且並不返回結果給調用者。而綁定service是由bindService()完成,它的作用在於能夠讓組件之間進行交互,發送請求、獲取結果以及完成進程間通信的任務,這種service在其他組件綁定它時會一直運行,直到所有組件取消綁定才銷燬。當然,你也可以在實現service時支持這兩種模式,僅僅需要你實現對應的回調函數即可:onStartCommand()和onBind()。
注意,service運行在進程的主線程中,並不會創建新的線程也不會運行在單獨的進程中(除非你指定)。意味着如果你的service要完成CPU密集型的工作或阻塞的操作,那麼你應該在service中創建新的線程來完成,這樣可以降低ANR(應用程序無法響應)的風險,同時主線程仍然用來用戶界面交互。
創建service
創建service必須創建Service子類,在實現中要重寫一些關鍵的回調函數,如onStartCommand()、onBind()、onCreate()和onDestroy(),附上簡要介紹:
onStartCommand():調用startService()時系統會調用onStartCommand()方法,一旦該方法執行,service便已經啓動且能在後臺運行。如果實現了該方法,需要程序員自行調用stopSelf()或stopService()來停止服務。
onBind():調用bindService()時系統會調用onBind()方法,在該方法的實現中需要返回一個IBinder,爲客戶端與service通信提供接口。該方法必須要實現,如果不需要進行綁定則返回null。
onCreate():在service第一次創建時會調用onCreate()方法,執行一次性的設置操作,當服務已經在運行時,該方法不再被調用。
onDestroy():當service被銷燬時,需要實現onDestroy()方法來清除所有資源,如線程、註冊的監聽器等。
與activity類似,service組件也需要在AndroidManifest.xml中進行聲明,在<application>元素中添加<service>子標籤,如下:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
同樣,啓動service需要使用Intent,也支持顯式(需要指定service類名)和隱式(需要指定<intent-filter>)兩種方式。創建started service可以繼承Service或IntentService。其中,Service類是所有service的基類,繼承Service類需要在service中創建新的線程來完成任務,因爲它使用應用程序的主線程。IntentService類是Service類的子類,它使用工作線程依次處理所有啓動請求,如果不需要同時處理多個請求,這種方式會是最好的選擇,你需要做的只是實現onHandleIntent()方法,用來接收所有請求的intent從而完成相應工作。
IntentService完成了以下工作:
(1)默認創建一個工作線程來執行所有傳入onStartCommand()的intent請求;
(2)創建一個工作隊列,它會每次向onHandleIntent()傳入一個intent,這樣就不用擔心多線程的問題;
(3)在所有請求被處理完成後,停止service,因此你不需要自行調用stopSelf();
(4)默認提供的onBind()實現會返回null;
(5)默認提供的onStartCommand()會將intent傳向工作隊列,然後傳向你所實現的onHandleIntent()。
總之,你只需要實現onHandleIntent(),可能還需要實現構造函數,參考下面的例子:
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
注意:如果要重寫IntentService類的其他方法(onBind()除外),如onCreate()、onStartCommand()或onDestroy(),那麼一定要調用父類的實現,這樣IntentService才能正常執行工作線程的生命週期。如重寫onStartCommand()方法:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
相對繼承IntentService而言,繼承Service會稍微複雜的多,但它所處理的情況通常是執行多線程任務,參考下面的例子:
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
注意到onStartCommand()方法的返回值是int類型,文檔上規定onStartCommand()方法只能返回如下常量中一個:
START_NOT_STICKY:在onStartCommand()返回後系統殺死service時不會重建service,除非有pending intent要發送。避免在不必要時運行service,應用程序可以隨時重啓任何未完成的任務。
START_STICKY:在onStartCommand()返回後系統殺死service時會重建service並調用onStartCommand(),但不會重新傳送上次的intent。在這種情況下,onStartCommand()接受的intent爲null,除非有pending intent要啓動service。它適用於媒體播放器或類似服務,不執行命令,但會等待任務。
START_REDELIVER_INTENT:在onStartCommand()返回後系統殺死service時會重建service並調用onStartCommand(),傳入上次的intent,任何pending intent會輪流發送。它適用於需要立即重啓的任務,如下載文件。
啓動Service
調用startService()並傳入intent,如顯式啓動HelloService:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
這種情況下,startService()會立即返回,系統會調用onStartCommand(),如果service未啓動會先調用onCreate(),再調用onStartCommand()。
如果希望service返回結果,創建PendingIntent傳入startService(),service會使用廣播來發送結果。
停止service
一個started service要維護它的生命週期。只有當系統需要回收內存時纔會停止或銷燬service,service在onStartCommand()返回後仍然保持運行狀態。因此,service自身要調用stopSelf()停止,或其他組件調用stopService(),這樣系統會盡快銷燬service。
當service同時處理多個啓動請求時,你不能在處理完一個啓動請求後停止service,因爲有可能已經接收到了一個新的啓動請求。爲了避免這個問題,可以調用stopSelf(int)方法,它會匹配最新請求的ID,保證所有請求處理完畢後停止service。
創建Bound Service
bound service允許應用程序組件調用bindService()進行綁定,允許應用程序其他組件與service進行交互,或者通過IPC使應用程序功能暴露給其他應用程序。創建bound service要實現onBind()回調函數,它會返回IBinder對象,該對象定義了與service通信的接口。當客戶端完成與service交互後,調用unbindService()解除綁定。由於創建Bound Service比創建Started Service更加複雜,後面會有單獨的一篇博文予以介紹。
在前臺運行Service
前臺service可以理解爲用戶所關心的,在內存緊缺時系統不會殺死的service。前臺service必須在狀態欄(status bar)提供一個notification,該notification不能夠被移除,除非service停止或者從前臺刪除。
請求service運行在前臺,要調用startForeground(),該方法有兩個參數:一個int值唯一標識notification(不能爲0),還有一個狀態欄的Notification,例如:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
從前臺刪除service,調用stopForeground(),該方法需要指定一個boolean值,表示是否要刪除狀態欄notification,該方法不會停止service。然而,如果service還在前臺運行時停止了service,此時notification也會被刪除。
Service生命週期
雖然Service生命週期比Activity生命週期要簡單得多,但是還是值得我們關注,因爲用戶無法感知到service的存在,它會在後臺運行。
鑑於service有started和bound兩種,簡單回顧一下:
started service:其他組件調用startService()時創建service,此後service會一直運行,直到自己調用stopSelf()或者其他組件調用stopService()停止service,系統最終會銷燬service。
bound service:其他組件調用bindService()時創建service,客戶端通過IBinder來與service進行通信,調用unbindService()可以關閉與service之間的連接,當所有組件解除與service之間的綁定時,系統會銷燬service。
這兩者也不是完全分離的,你可以綁定已經由startService()啓動的service。例如,後臺音樂服務可以由startService()啓動,intent指定要播放的音樂,接下來如果用戶要對播放器進行控制或者獲取當前歌曲信息,該activity可以調用bindService()綁定到service上。在這種情況下,stopService()和stopSelf()並不能停止服務,只有等到所有客戶端解除綁定纔會停止服務。
與activity類似,service也有一系列生命週期回調函數,實現這些函數能夠監聽service的狀態並在適當的時候執行任務,下面的例子給出了這些回調函數:
public class ExampleService extends Service {
int mStartMode; // indicates how to behave if the service is killed
IBinder mBinder; // interface for clients that bind
boolean mAllowRebind; // indicates whether onRebind should be used
@Override
public void onCreate() {
// The service is being created
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public void onDestroy() {
// The service is no longer used and is being destroyed
}
}
需要注意的是,與activity不同之處在於,在自定義的service中不允許調用父類的這些回調函數。
如上圖爲service的生命週期圖。左邊表示調用startService()的生命週期,右邊表示調用bindService()的生命週期。
參考資料:Google ADT doc
Except as noted, this content is licensed
under Creative
Commons Attribution 2.5.