一個Service是一個應用組件,它能夠在後臺執行耗時長的操做,而不提供用戶接口。另外一個應用組件能夠啓動一個service,而後它將在後臺持續運行,即便用戶切換到了另外一個應用。此外,一個組件能夠bind到一個service來與之交互,甚至執行進程間通訊(IPC)。好比,一個service能夠處理網絡事務,播放音樂,執行文件I/O,或者與一個content provider交互,均是在後臺。html
一個service實質上能夠有兩種形式:java
Startedandroid
當一個應用組件(好比一個activity)經過調用startService()來啓動一個service時,則service是"started"的。一旦被啓動,一個service能夠在後臺無限期地運行,即便啓動它的組件已經被銷燬了。一般一個被啓動的service執行一個單獨的操做,而且不給調用者返回一個結果。好比,它能夠經過網絡下載或上傳一個文件。當操做完成時,那個service應該自動中止。數據庫
Bound安全
當一個應用組件經過調用bindService()來bind一個service時,則service是 "bound"的。一個bound service提供了一個容許組件與service交互的客戶端-服務器接口,發送請求,獲取結果,甚至經過進程間通訊(IPC)來誇進程執行這些。一個bound service只有在另外一個應用組件被bound到它時才運行。多個組件能夠當即bind到service,但當它們都unbind時,那個service將會銷燬。服務器
儘管這份文檔分開討論了這兩種類型的service,但你的service能夠同時以這兩種方式運行——它能夠被啓動(來無限期的運行)並容許binding。這只是你是否實現了一對回調方法的問題:實現onStartCommand()以容許組件啓動它,實現onBind()以容許binding。網絡
不管你的應用是被啓動的,bound的,或者二者都有,任何應用組件均可以以與任何組件使用一個activity相同的方式來使用這個service(甚至是從另一個應用程序中)——經過一個Intent來啓動它。然而,你能夠在manifest文件中聲明service爲私有的,從而阻止來自於其餘應用的訪問。這將會在關於Declaring the service in the manifest的部分進行更多的討論。多線程
注意: 一個service是在它的宿主進程的主線程中運行的——service不建立它本身的線程,也不在分開的進程中運行的(然而除非你那樣指定)。這意味着,若是你的service要執行任何CPU密集的操做或者阻塞操做(好比MP3回放或網絡),你應該在service中建立一個新的線程來執行那項工做。經過使用一個分開的線程,你將減小發生ANR錯誤的風險,而應用的主線程依然能夠專門用於與你的activities進行用戶交互。併發
爲了建立一個service,你必須建立一個Service(或它的一個已經存在的子類)的子類。在你的實現中,你須要覆寫一些回調方法來處理service生命週期的某些重要的方面,併爲組件提供一種機制來bind到service,若是合適的話。你應該覆寫的最重要的回調方法爲:app
當另外一個組件,好比一個activity,經過調用startService()請求啓動service時,系統會調用這個方法。這個方法一旦執行,則service被啓動並能夠在後臺無限期地運行。若是你實現了這個方法,則你有責任在它的工做完成時,經過調用stopSelf()
或stopService()來中止它。(若是你只想提供binding,則你不須要實現這個方法。)
當另外一個組件經過調用bindService()想要bind到service時(好比執行RPC),系統會調用這個方法。在你的這個方法的實現中,你必須經過返回一個IBinder來提供一個客戶端同於與service通訊的接口。你必須老是實現這個方法,但若是你不想提供binding,則你應該返回null。
當service第一次啓動時,系統會調用這個方法,來執行一次性的設置過程(在它調用onStartCommand()或onBind()以前)。若是service已經在運行了,則這個方法不會被調用。
當service再也不被用到並在被銷燬時,系統會調用這個方法。你的service應該實現這個方法來清理全部的資源,好比線程,註冊的listeners,receivers,等等。這是service接收到的最後一個調用。
若是一個組件經過調用startService()啓動了service(這將致使一個對於onStartCommand()的調用),則service將持續運行直到它經過stopSelf()來中止或者另外一個組件經過調用stopService()來中止它。
若是一個組件調用了bindService()來建立service(onStartCommand()不會被調用),則service只有在組件bound到它的過程當中才運行。一旦全部的客戶端都unbound了service,則系統會銷燬它。
只有當memory不足時Android系統纔會強制中止一個service,它必須恢復那個具備用戶焦點的activity的系統資源。若是service被bound到一個具備用戶焦點的activity,則它就不太可能被殺掉,而若是service被聲明爲 前臺運行(稍後討論),則它將幾乎從不被殺掉。然而,若是service被啓動並長時間運行,則系統將降隨着時間推移低它在後臺任務列表的位置,service將變得極可能被殺掉——若是你的service被啓動了,你必須設計它來優雅地處理系統重啓它的狀況。若是系統殺掉了你的service,它將在資源變得再次可用時當即重啓它(儘管這也依賴於你從onStartCommand()返回的值,稍後討論)。更多關於系統在何時可能會殺掉一個service的信息,請參考Processes和Threading 文檔。
在下面的幾個小節中,你將看到如何建立每種類型的service,及如何在其餘的應用組件中使用它。
像activities(及其餘的組件)同樣,你必須在你的應用程序的manifest文件中聲明全部的services。
要聲明你的service,添加一個<service>元素做爲
<application>元素的子元素。好比:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
參見<service>元素參考來獲取更多關於在manifest中聲明你的service的信息。
你能夠在<service>元素中包含一些其餘的屬性來定義諸如啓動service所需的權限,service應該運行其中的process等屬性。
android:name
是屬性僅有的必須的屬性——它描述了service的類名。一旦你發佈了你的應用,你就不該該再改動這個名字了,由於若是你改了的話,你可能會破壞那些依賴於明確的intent來啓動或bind service的代碼(閱讀博客文章,Things That Cannot Change)。
爲了確保你的app是安全的,請在啓動或binding你的Service時
老是使用一個明確的intent,而不是爲service聲明intent filters。關於具體要啓動哪一個service,若是容許一些模糊性很重要,你能夠爲你的service提供intent filters,並從Intent中排除組件的名字,但你必須隨後經過setPackage()來爲intent設置包名,這將爲目標service消除歧義。
此外,你能夠經過包含android:exported屬性並將它設爲「false」來確保只有你的app能夠訪問你的service。這有效的阻止了其它apps啓動你的service。
一個started service是另外一個組件經過調用startService()來啓動,會致使一個對於service的onStartCommand()方法的調用的service。
當一個service被started了,它將具備一個獨立於啓動它的組件的生命週期,而且能夠在後臺無限期的運行,即便啓動它的組件被銷燬了。所以,service應當在它的工做結束時經過調用stopSelf()來自動中止,或者另外一個組件能夠經過調用stopService()來中止它。
一個應用組件好比一個activity能夠經過調用startService()來啓動service,傳遞一個Intent來指定service幷包含一些service將會使用的數據。service在onStartCommand()方法中接收這個Intent。
好比,假設一個activity須要在一個線上數據庫保存一些數據。則那個activity能夠啓動一個夥伴service,並經過給startService()傳遞一個intent來將要保存的數據傳遞過去。service在onStartCommand()中接收intent,鏈接到Internet並執行數據庫事務。當事務完成時,service自動中止並被銷燬。
注意: 默認狀況下,一個service所運行的進程與聲明那個service的應用的進程是相同的,而且它仍是在那個應用的主線程中運行。所以,若是你的service在用戶與相同的應用中的一個activity進行交互時,執行計算密集的或阻塞的操做,則service將下降activity的性能。爲了不影響應用的性能,你應該在service的內部新起一個線程。
傳統上,你能夠擴展兩個類來建立一個started的service:
這是全部services的基類。當你擴展這個類時,很重要的一點是,你要建立一個新的線程來執行service的全部工做,由於默認狀況下service使用了你的應用的主線程,這將下降你的應用所運行的全部的activity。
這是一個Service的子類,它使用了一個工做者線程來處理全部的start請求,每次一個intent。若是你不須要你的service並行的處理多個請求的話,這是最好的選擇。你所須要作的就是實現onHandleIntent(),每次的start請求它都接收intent,以使你能夠執行後臺工做。
下面的小節將描述你能夠如何使用一個或多個這樣的類來實現你的service。
因爲大多數started services不須要並行地處理多個請求(這實際上多是一個危險的多線程場景),使用IntentService類來實現你的service多是最好的。
IntentService
作了以下的這些:
在你的應用的主線程以外建立一個默認的工做者線程執行傳遞給onStartCommand()的全部的intents。
建立一個工做隊列,來每次給你的onHandleIntent()實現傳遞一個intent,以使你從不須要擔憂多線程。
在全部的start 請求都被處理了以後,終止service,所以你從不須要調用stopSelf()。
提供了一個默認的返回null的onBind()實現。
提供了一個默認的onStartCommand()實現,來發送intent給工做隊列,繼而發送給你的onHandleIntent()實現。
總結一下就是,你所須要作的一切就是實現onHandleIntent()來完成客戶端提供的工做。(儘管你也須要爲service提供一個小的構造函數。)
這裏有一個IntentService的實現的例子:
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) { } } } } }
這就是你所須要的一切:一個構造函數和一個onHandleIntent()的實現。
若是你決定也要覆寫其餘的回調方法,好比onCreate()
,onStartCommand()
,或onDestroy(),要確保調用了父類的實現,以使得IntentService能夠適當地處理工做者線程的生命週期。
好比,onStartCommand()必須返回默認的實現(其中處理了intent如何被傳遞給onHandleIntent()的過程):
@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);
}
除了onHandleIntent()以外,僅有的在其實現中你不須要調用超類實現的方法是onBind()(但只有在你的service容許binding時才須要實現。)
在下一小節,你將看到當擴展基本的Service類時,相同種類的service如何實現,其中須要更多的代碼,但若是你須要併發地處理start請求則可能更合適一點。
如你在前一小節所見,使用IntentService使得你的一個started service實現變得很簡單。若是,然而,你的service要執行多線程(而不是經過一個工做隊列來處理start請求),則你能夠擴展Service類來處理每一個intent。
爲了便於對比,下面的示例代碼是一個Service類的實現,其執行的工做與上面使用IntentService的例子徹底同樣。即,對於每一個start請求,它使用一個工做者線程來執行工做,而且每次只處理一個請求。
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()的調用,你能夠並行地執行多個請求。那不是這個例子所演示的,但若是那是你想要的,則你能夠爲每一個請求建立一個新的線程,並當即執行它們(而不是等待前面的請求結束)。
注意onStartCommand()方法必須返回一個整數。那個整數是一個值,其描述了在系統要殺掉一個service的事件中系統應該如何繼續那個service(如上面討論的,IntentService的默認實現爲你處理了這些,儘管你也能夠修改它)。onStartCommand()的返回值必須是下列常量中的一個:
若是在onStartCommand()返回以後系統殺死了service,不要從新建立那個service,除非有掛起的intents傳送過來。這是最安全的選項來避免在你的service在不須要或你的應用能夠簡單地重啓任何未完成的工做時運行你的service。
若是在onStartCommand()返回以後系統殺死了service,從新建立service並調用onStartCommand(),但不要從新傳送上一個intent。相反,系統以一個null intent來調用onStartCommand(),除非有掛起的intent來啓動service,在這種狀況下,那些intents會被傳送。這對媒體播放器(或相似的services)比較合適,它們不執行命令,但會無限期的運行並等待一個任務。
若是在onStartCommand()返回以後系統殺死了service,從新建立service,並以傳送給service的最近的intent調用onStartCommand()。其餘的掛起的intents則依序傳送。對於那些應該被當即恢復地活躍地執行一個任務的service比較合適,好比下載一個文件。
關於這些返回值的更詳細信息,請參考每一個常量連接的參考文檔。
你能夠在一個activity或其它的應用組件中,經過傳遞一個Intent(指定了要啓動的service)給startService()來啓動一個service。Android系統調用service的onStartCommand()方法並給它傳遞那個Intent。(你毫不應該直接調用onStartCommand()。)
好比,一個activity能夠經過startService()使用一個顯式的intent來啓動上一小節中的示例service(HelloSevice):
Intent intent = new Intent(this, HelloService.class);
startService(intent);
startService()會當即返回,而且Android系統會調用service的onStartCommand()方法。若是service尚未在運行,則系統將首先調用onCreate(),而後調用onStartCommand()。
若是service不提供binding,則經過startService()傳送的intent將是在應用組件和service之間通訊的惟一模式。然而,若是你想要service發回一個結果,則啓動service的客戶端能夠給一個broadcast建立一個PendingIntent(經過getBroadcast()),並在啓動service的Intent中把它傳遞給service。而後service可使用broadcast來傳送一個結果。
多個啓動service的請求會致使對於service的onStartCommand()的多個對應的調用。然而,要中止service(經過stopSelf()或stopService())則只須要一個請求。
一個started service必須管理它本身的生命週期。即,系統不會中止或銷燬service,除非它必需要恢復系統內存,而且service在onStartCommand()返回後也將繼續運行。所以,service必須經過調用stopSelf()來中止它本身或另外一個組件能夠經過調用stopService()來中止它。
一旦經過 stopSelf()
或stopService()被請求中止了,則系統會盡快銷燬那個service。
然而若是你的service併發地處理多個到onStartCommand()的請求,則你不該該在完成處理一個start請求時中止service,由於你可能已經接收了一個新的請求(在第一個請求的結尾處中止將終止第二個)。爲了不這個問題,你可使用stopSelf(int)來確保你的中止service的請求老是基於最近的start請求。即,當你調用stopSelf(int)時,你傳遞你的中止請求所對應的start請求的ID(startId傳送給了onStartCommand())。而後若是service在你可以調用stopSelf(int)前接收了一個新的start請求,則ID將不匹配,而service將不會中止。
注意:很重要的一點是,你的應用要在工做完成後中止它的service,以免浪費系統資源及消耗電源。若是須要的話,其它的組件能夠經過調用stopService()來中止service。即便你容許binding那個service,若是曾經接收了一個對於onStartCommand()的調用,你也必須老是親自中止service
更多關於一個service的生命週期的信息,請參考下面的關於管理一個service的生命週期的部分。
一個bound service是一個容許應用程序組件經過調用bindService()來bind到它以建立一個長期的鏈接(一般來講不容許組件經過調用startService()來start)。
當你想要在你的應用的activities或其它組件中與service交互,或要經過進程間通訊(IPC)將你的應用的功能暴露給其餘應用時,你應該建立一個bound service。
要建立一個bound service,你必須實現onBind()回調方法來返回一個IBinder,然後者則定義了與service通訊的接口。隨後,其它的應用組件能夠調用bindService()來獲取接口並開始調用service上的方法。service只有在服務於bound到它的應用組件時纔是存活的,所以當沒有組件bound到service時,則系統會銷燬它(你不須要中止一個bound,像經過onStartCommand()啓動的service中那樣的方式)。
要建立一個bound service,你必需要作的第一件事情是定義接口,其描述了一個客戶端能夠如何與service通訊。在service和一個客戶端之間的接口必須是一個IBinder的實現,而且是你的service必須在onBind()回調方法中返回的東西。一旦客戶端接收了IBinder,它能夠開始經過那個接口來與service交互。
多個客戶端能夠同時bind到service。當一個客戶端完成了與service的交互,它能夠調用unbindService()
來unbind。一旦沒有客戶端bound到了service,則系統會銷燬那個service。
有多種方法來實現一個bound service,而且那樣的實現相對於一個started service要更加的複雜,所以bound service將在另外的一份關於Bound Services的文檔中來討論。
一旦執行起來的,則一個service可使用Toast通知或狀態欄通知來給用戶通知事件。
一個toast通知是一個消息,它將出如今當前窗口的表面一小段時間而後消失,而一個狀態欄通知在狀態欄中顯示一個消息,同時還有一個圖標,用戶能夠選擇它們來執行一個動做(好比啓動一個activity)。
一般,一個狀態欄通知是當後臺工做完成時(好比一個文件下載完了)可用的最好的技術,用戶能夠在他上面執行一些動做。當用戶在expanded view中選中了通知,通知能夠啓動一個activity(好比查看下載的文件)。
A foreground service is a service that's considered to be something the user is actively aware of and thus not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the "Ongoing" heading, which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
For example, a music player that plays music from a service should be set to run in the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the user to launch an activity to interact with the music player.
To request that your service run in the foreground, call startForeground()
. This method takes two parameters: an integer that uniquely identifies the notification and the Notification
for the status bar. For example:
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);
Caution: The integer ID you give to startForeground()
must not be 0.
To remove the service from the foreground, call stopForeground()
. This method takes a boolean, indicating whether to remove the status bar notification as well. This method does not stop the service. However, if you stop the service while it's still running in the foreground, then the notification is also removed.
For more information about notifications, see Creating Status Bar Notifications.
一個service的生命週期比一個activity的要簡單的多。然而密切注意你的service如何被建立和銷燬則甚至更加劇要,由於一個service能夠在用戶意識不到的狀況下在後臺運行
service的生命週期——從它什麼時候被建立到什麼時候被銷燬——可能遵循兩個不一樣的路徑:
一個started service
當另外一個組件調用了startService()時,service被建立。而後service就無限期的運行,它必須經過調用stopSelf()來中止它本身。另外一個組件也能夠經過調用stopService()來中止那個service。當service被中止時,系統將銷燬它。
一個bound service
當另外一個組件(一個客戶端)調用了bindService()時service被建立。而後客戶端經過一個IBinder接口來與service通訊。客戶端能夠經過調用unbindService()來關閉鏈接。多個客戶端能夠bind到相同的service,當全部的客戶端都unbind時,系統銷燬service.(Service不須要中止它本身。)
這兩個路徑不徹底是分開的。即,你能夠bind到一個已經經過startService()被started的service。好比,一個後臺的音樂服務能夠經過調用startService()來啓動,其Intent來描述要播放的音樂。隨後,用戶可能想要使用一些關於播放器的控制或獲取關於當前歌曲的信息,一個activity能夠經過調用bindService()來bind到service。在相似這樣的狀況下,stopService()
或stopSelf()不會真的中止service,直到全部的客戶端都unbind。
像一個activity同樣,一個service具備生命週期,你能夠實現它們來監視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的生命週期回調方法,你不須要調用這些回調方法的超類實現。
圖 2. Service的生命週期。 左邊的圖顯示了經過startService()來建立的service的生命週期,右邊的圖顯示了經過bindService()建立的service的生命週期。
經過實現這些方法,你能夠監視service的生命週期的兩個嵌套循環:
.一個service的完整生命週期發生在onCreate()被調用和onDestroy()返回之間。如同一個activity,一個service在onCreate()中執行它的初始化設置,並在onDestroy()中釋放全部剩餘的資源。好比,一個音樂回放service能夠在onCreate()中將要播放音樂的地方建立線程,而後在onDestroy()中中止線程。
全部的services的onCreate()
和onDestroy()都會被調到,不管它們經過startService()仍是bindService()建立的。
一個service的活躍生命週期從調用onStartCommand()
或onBind()開始。傳遞給startService()或bindService()的Intent會分別被傳給每一個方法。
若是service被started的,活躍生命週期將與完整生命週期在相同的時間點結束(即便是在onStartCommand()返回後,service也依然是活着的)。若是service是被bound的,則活躍生命週期將在onUnbind()返回時結束。
Note注意:儘管一個started的service經過一個調用stopSelf()或stopService()來中止,但service卻沒有一個專門的對應的回調(沒有onStop()回調)。所以,除非service被bound到一個客戶端,系統將在它被中止時銷燬它——onDestroy()是僅有的接收到的回調。
圖2描繪了一個service的典型的回調方法。儘管圖中將經過startService()建立的service和那些經過bindService()建立的service分開了,但要記住,任何service,不管它是如何被啓動的,均可能潛在的容許客戶端bind到它。所以,一個最初經過onStartCommand()啓動的service(經過一個客戶端調用startService())依然能夠接收一個到onBind()的調用(當一個客戶端調用bindService()時)。
更多關於建立一個提供binding的service的信息,請參考Bound Services 文檔,其中在關於Managing the Lifecycle of a Bound Service的小節中包含了更多關於onRebind()回調方法的信息。