Service 概述
關於 Service 的介紹,官方文檔裏面有給詳細的說明,它是一個可以再用戶看不到的情況下長時間執行某項任務的應用組件。它可以由 Activity 通過調用 startService()
方法來啓動,也可以跟 Activity 進行綁定,Activity 也可以通過綁定到 Service 來與其進行交互。
一般情況下,Service 會有兩種狀態:
- 啓動狀態:Activity 調用 startService 啓動服務之後, Service 便位於這個狀態。這種狀態下,服務會在後臺無限期的運行,知道服務的任務執行完畢,一般我們啓動服務用於執行某項任務,任務執行完了之後便會退出,且不會反悔結果。
- 綁定狀態:應用組件調用了bindService之後,服務就會處於該狀態,此時服務會提供一個客戶端的示例,通過該示例可以與Service進行交互,一般用於跨進程通信。一個服務可以同時被多個組件綁定,但是隻有當所有綁定的組件都銷燬是,這個服務纔會銷燬。
上面的兩種狀態在 Service 中是可以共存的,即一個服務啓動的同時也可以被綁定。區別是啓動服務會回調 Service
中的 onStartCommand
方法,而綁定則會回調 onBind()
方法。在 Android 中的任何組件都可以通過 Intent 來使用服務,即使在不同的應用中,當然這需要你定義的 Service 是對其他應用開放的。
Android 中的四大組件都是需要在 AndroidManifest.xml 中進行聲明瞭纔可以可使用,因此當你創建一個服務時,在清單文件也需要使用<service>
標籤來聲明。
默認情況下,Service是在啓動它的應用程序進程中運行的,它不會創建自己的線程,因此如果要執行耗時的任務時,爲了不阻塞應用程序的主進程,我們應該在 Service 中開啓一個子線程。
Service 的使用
繼承 Service 類
要創建一個自己的服務很簡單,只需繼承 Service 類並重寫裏面的方法即可。示例如下:
public class MyService extends Service{
public MyService(){
}
//若沒有重寫該方法,服務將無法綁定到其他組件
@Override
public IBinder onBind(Intent intent){
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate(){
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId){
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestory(){
super.onDestory();
}
}
上面各個方法的含義如下:
- onCreate(): 只有第一次創建服務的時候會調用。
- onBind(): 前面說過其他組件可以可通過調用
bindService()
來綁定服務,該方法就是在服務被綁定的時候調用,這個方法會返回一個 IBinder 接口的實例,其他組件就是通過這個組件來與服務進行通信。 - onDestory(): 當服務被銷燬時被調用,一般在這個回調中進行資源的清理。
- onStartCommand(): 當別的組件通過
startService()
方法來啓動服務的時候,會回調這個方法。一般在這個方法中執行任務的邏輯,注意執行完了之後記得在該方法中調用stopSelf()
或者stopService()
來停止服務。該函數必須返回整型值,用於描述當服務終止時,如何繼續運行服務,必須是以下常量之一:START_NOT_STICKY
:表示如果在onStartCommand()
返回後才停止服務,則系統不會重新創建服務,除非有新的Intent傳遞到該服務START_STICKY
:表示如果該服務被系統 killed 了,該服務會一直保留啓動的狀態,在下次創建服務時則一定會重新調用onStartCommand()
方法,即使沒有啓動服務的命令。START_REDELIVER_INTENT
:如果該服務給系統 killed ,將會重新啓動,並且最後一次用於啓動服務的 Intent 會被重新傳遞給它。
在AndroidManifest.xml中聲明
創建了 Service 的子類以後還要將該服務在清單文件(AndroidManifest.xml)中聲明,代碼如下:
<manifest>
......
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
........
</manifest>
幾個屬性的含義如下:
- android:name: 用於指定服務的類名
- android:enabled: 是否啓用該服務,默認爲是(true)
- android:exported: 指定服務是否能被其他應用程序所調用。
啓動服務
一般通過將要啓動的服務的類名傳遞到 Intent 中,在將該 Intent 作爲 startService()
的參數來啓動服務的。示例如下:
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
startService()
方法調用了之後會執行 Service 裏面的 onStartCommand()
方法。如果多次發起啓動服務的請求,則會調用多次該方法。啓動服務之後會無限制的運行,即使啓動服務的組件已經被銷燬
停止服務
前面也說過一旦服務啓動了之後便會一直運行,因此要麼會在組件銷燬時調用 stopService()
停止服務,要麼在Service的任務執行完畢之後,調動stopSelf()
自行停止,依次來保證系統的資源能夠被釋放。如果服務同時處理多個 onStartCommand()
,則應該調用 stopSelf(int)
來停止最近的服務請求。
綁定服務
Android 提供了 bindService()
來進行服務的綁定,以便於長期連接。服務必須實現 onBind()
方法才能進行服務的綁定,該方法返回一個 IBinder 接口實例,用於與服務進行通信。當沒有組件綁定到服務時,該服務則會被系統銷燬。多個客戶端可以同時綁定到一個服務,並且可以通過 unbindService()
來解綁。
前臺服務
前臺服務時用戶主動意識到的服務,因此在內存不足時,系統也不會銷燬它。當然前臺服務必須以在狀態欄上提供通知。 Android 提供了 startForeground(int id, Notification notification) 方法來將一個服務變成前臺服務,該方法有兩個參數:
id
:通知的唯一標識符,不能爲0,整型notification
:狀態欄的notification
移除前臺服務調用 stopForeground()
服務的生命週期
官方給出的服務生命週期圖如下:
- 正常生命週期從
onCreate()
到onDestory()
,與 Activity 差不多 - 服務的有效生命週期則是從
onStartCommand()
或者onBind()
,兩者分別對應啓動服務和綁定服務,啓動服務的有效生命週期與整個生命週期同時結束(即回調 onDestory() 時)。而綁定服務的有效生命週期則在onUnbind()
返回時結束。
不管是以何種方式啓動服務的,均有可能與客戶端進行綁定,因此即使已經使用 onStartCommand() (即客戶端調用 startService )啓動的服務仍可以接收對 onBind() 的調用(客戶端調用 bindService() )
IntentService
IntentService 也是Service的子類,對 Service 做了一定的封裝,前面說了如果不手動給 Service 開啓一個線程的話,默認會運行在主線程中,這會降低正在運行的Activity的性能。因此 Android 提供了 IntentService ,它會創建獨立的 Worker 線程來處理請求。
IntentService 的使用方法也很簡單,只需繼承 IntentService 類並重寫 onHandleIntent()
方法即可
IntnetService 的主要執行了以下操作:
- 創建獨立的工作線程來處理原本交給
onStartCommand()
的 Intent - 創建工作隊列,將請求逐一傳遞給
onHandleIntent()
去處理。 - 處理完請求之後會自動停止服務,不需要手動執行
stopSelf()
- 默認實現
onBind()
,返回 null - 默認實現
onStartCommand()
,將請求一次發送到工作隊列然後在onHandleIntent()
中去處理
public class HelloIntentService extends IntentService {
public HelloIntentService(){
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId){
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(Intent intent, int flags);
}
}
參考文檔
- Android Developers Service
- 《第一行代碼》 郭霖著