Develop系列-API Guides-應用組件-Services,-api-services
Services
Services:可在後台長時間運行,無UI介面。
Started:
Services通過其他組件調用startService啟動,能在後台一直運行,即使啟動它的組件已經銷毀。一般這種服務執行特定的操作而且沒有傳回值,比如通過網路下載或上傳一個檔案,一旦操作結束,服務需要停止自己。
Bound:
Service通過其他組件調用bindService綁定,提供一個CS架構的介面允許其他組件與Service互動、發訊息、擷取傳回值、甚至跨進程通訊。bound service與綁定它的組件共存亡,多個組件可以綁定一個服務,當這個服務所有綁定它的組件都解除綁定後,服務會被銷毀。
Caution:Service在宿主進程的主線程運行,也就是說Service不會建立自己的線程,也不會運行在另一個進程(除非你指定)。這就意味著,如果Service執行一些高CPU負載或阻塞介面操作的任務時,你需要建立新的線程來運行它,以降低ANR的風險。
(使用service還是thread?如果你只是需要在與使用者互動時,在主線程之外執行額外的任務,那麼你需要建立一個新線程而不是Service。比如在activity在前台顯示的時候播放音樂,你需要在onCreate裡面建立線程,在onStart執行線程,在onStop裡停止線程,當然也可以使用AsyncTask或者HandleThread來替代線程類。)
基礎
建立Service的子類,覆寫Service生命週期中的關鍵回呼函數,如果有必要,提供綁定Service的機制。最重要的回呼函數如下:
onStartCommand
系統在其他組件調用startService啟動Service時執行,之後,Service一直在後台執行。如果你實現了這個方法,那麼你有責任在Service完成其工作時通過stopSelf或stopService來停止Service。(如果只是提供綁定Service,可以不用實現這個方法)
onBind
系統在其他組件調用bindService綁定Service時執行,在這個方法的實現中,你需要返回一個IBider介面用於clients與service互動。如果你不需要綁定,可以直接返回null
onCreate
Service第一次建立時執行,在onStartCommand和onBind之前,用於執行一次性需要初始化好的資料。
onDestroy
service銷毀時執行,用於清理資源,類似線程、註冊監聽器,receiver等。
系統會在低記憶體時強制停止service。如果service綁定在一個有使用者焦點的activity,那麼它很少會被銷毀;如果服務定義為在前台啟動並執行,那麼它永遠不會被銷毀。
在manifest定義service
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>
android:name是唯一必填的屬性,其他可以選填。一旦你的應用發布出去了,那麼android:name永遠不要再變。
為了安全性,請使用顯式Intent啟動或綁定服務,而不是定義filiters。如果確實允許多個服務來響應,那麼請務必在Intent中通過setPackage來指定報名,以降低目標服務匹配出意外值得機率。
設定android:exported為false,可以避免其他應用啟動這個服務。
建立service
Service
所有服務的基類,如果你擴充這個類,那麼你可能需要建立一個新的線程,因為這個服務室在app的主線程中執行,有可能會降低activity的流暢度
IntentService
Service 的子類,通過背景工作執行緒來處理所有請求,每次處理一個。如果你的service不需要同時處理多個請求,IntentService是最佳的選擇。
擴充IntentService
因為大部分服務不需要同時處理多個請求(實際上是個多線程情境),IntentService是最好的推薦
IntentService工作流程如下:
- 建立一個預設的新背景工作執行緒來執行所有分發給onStartCommand的intents
- 建立一個工作隊列來依次傳遞intent去執行onHandleIntent具體實現,所以不需要考慮多線程情境
- 所有請求執行完之後,自動銷毀,你不需要執行stopSelf
- 提供預設的onBind實現:直接返回null
- 提供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) { } } } }}
如果你需要覆寫其他回調,比如onCreate、onStartCommand、onDestroy,請確保要調用父類的實現,這樣IntentService才能處理背景工作執行緒的生命週期。onHandleIntent和onBind是例外。
擴充Service
如果服務需要支援多線程(而不是通過工作隊列來一一處理),那麼你要擴充Service來處理每一個intent。
作為對比,如下代碼和上面代碼功能一樣。
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(); }}
這個只是類比IntentService處理,有必要的話,你可以為每一個請求建立一個線程。
onStartCommand必須返回一個整型,這個值用來描述系統如何處理這個服務,當它被異常銷毀時。
START_NOT_STICKY
如果服務執行完onStartCommand後被異常銷毀,不需要重新建立服務,除非還有未處理的intents。避免服務被不必要地啟動,你的應用能夠很容易地重新啟動未完成的工作,選擇這個。
START_STICKY
如果服務執行完onStartCommand後被異常銷毀,重新建立服務並執行onStartCommand,但是不會使用發給這個服務的最後一個intent,而是通過一個null intent來執行onStartCommand,除非還有未處理的intents。這種情況比較適合不執行命令,一直在後台運行或者等待任務的媒體播放。
START_REDELIVER_INTENT
如果服務執行完onStartCommand後被異常銷毀,重新建立服務,用發給這個服務的最後一個intent調用onStartCommand,其他未處理的intents依次分發。這種情況適合執行的任務需要理解恢複的情境,比如下載檔案。
啟動服務
startService –> onStartCommand
Intent intent = new Intent(this, HelloService.class);startService(intent);
startService方法立即返回。
如果service不能綁定,只能通過startService啟動,而且需要發送傳回值時,可以通過建立帶延遲廣播的PendingIntent(用getBroadcast)來啟動服務,服務能用裡面的廣播來分髮結果。
停止服務
如果服務同步地處理多個請求,那麼不能在處理完一個請求直接銷毀,有可能其他的請求剛發過來或者還在處理中。為了避免這種情況,你可以通過stopSelf(int)來確保關閉的服務就是剛處理完請求的,而不是其他正在處理的服務,整型參數唯一識別服務執行個體。
建立綁定服務
詳見下一章節
發送Notifications給使用者
服務一旦運行,可以通過Toast Notifications和Status Bar Notifications提醒使用者。
在前台運行服務
在前台啟動並執行服務必須提供一個狀態列的notification,來呈現運行狀態,除非服務被停止或者移除,否則notification不會消失。
比如:播放音樂,在狀態列有個notification來告訴使用者當前播放的歌曲,提供使用者進入音樂播放器的入口
服務需要在前台運行,需要調用startForground,第一個參數是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);
停止在前台運行,可以調用stopForeground。
實現生命週期回調
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 }}
Note:不需要執行父類的實現。
完整的生命週期:onCreate —> onDestroy
活動的生命週期:onStartCommand/onBind —> onDestroy/onUnbind