一個bound service是一個client-server接口中的server端。一個bound service容許應用組件(好比activities)bind到它,發送請求,接收響應,甚至是執行進程間通訊(IPC)。一個bound service在典型狀況下,只有在它服務於另外一個應用組件時才存活,而不是在後臺無限期的運行。html
這份文檔向您說明了要如何建立bound service,包括在其餘的應用組件中如何bind到service。然而,你也應該參考Services文檔來大致地瞭解關於services的額外信息,好比如何在service中傳送通知,設置service在前臺運行,等等。java
一個bound service是一個Service類的實現,它容許其它應用bind到它並與它交互。爲了給一個service提供binding功能,你必須實現onBind()回調方法。這個方法返回一個IBinder對象,該對象則定義了客戶端能夠用來與service進行交互的編程接口。android
如同在Services文檔中討論的那樣,你能夠建立一個service,既能夠被started,也能夠被bound。即,service能夠經過調用startService()被started,從而容許service無限期的運行,也能夠容許一個客戶端經過調用bindService()來bind到service。編程
若是你確實容許你的service被started和bound,則當service已經被started時,系統不會在全部的客戶端都unbind時銷燬那個service。相反,你必須經過調用stopSelf()或stopService()來顯式地中止那個service。api
儘管你一般應該實現onBind()或onStartCommand()之一,但有時須要同時實現二者。好比,一個音樂播放器可能會發現同時容許它的service無限期的運行及提供binding是頗有用的。以這種方式,一個activity能夠start service來播放一些音樂,而在用戶離開了應用後音樂也能夠繼續播放。而後,在用戶回到應用時,activity能夠bind到service來恢復對回放的控制。安全
請確保閱讀了關於管理一個Bound Service的生命週期的部分,來了解更多關於給一個started service添加binding功能時service生命週期的信息。多線程
一個客戶端能夠經過調用bindService()來bind到service。當它bind時,它必須提供一個ServiceConnection的實現,其者監視與service之間的鏈接。bindService()將當即返回,而且沒有返回值,但當Android系統建立客戶端和service之間的鏈接時,它會調用ServiceConnection的onServiceConnected(),來傳送IBinder給客戶端用於和service進行通訊。併發
然而,系統只有在第一個客戶端binds時纔會去調用你的service的onBind()來獲取IBinder。隨後系統傳送相同的IBinder給其它bind的客戶端,而再也不次調用onBind()。app
當最後一個客戶端從service unbinds時,系統將銷燬service(除非service也經過startService()被started了)。dom
當你實現你的bound service時,最重要的部分是定義你的onBind()回調方法返回的接口。你能夠用一些不一樣的方法來定義你的service的IBinder接口,下面的小節將討論每種技術。
當建立一個提供binding功能的service時,你必須提供一個客戶端能夠用於與service端交互的提供了編程接口的IBinder。有三種方法你能夠用來定義這樣的接口:
若是你的service是你本身的應用私有的,而且與客戶端運行在相同的進程中(很常見的狀況),你應該經過擴展Binder類來建立你的接口,並在onBind()方法中返回一個它的實例。客戶端接收到Binder,而後就可使用它來直接訪問Binder實現或Service的可用的public方法。
當你的service只是你本身的應用的一個後臺工做者時,這是首選的技術。不使用這種方式來建立你的接口的僅有的理由就是,你的service被其餘應用或跨越不一樣的進程被用到了。
若是你須要你的接口跨進程工做,你能夠藉助於一個Messenger來爲你的service建立一個接口。用這種方式,service定義了一個Handler,來響應不一樣類型的Message對象。這個Handler是一個Messenger的基礎,而後後者能夠與客戶端共享一個IBinder,容許客戶端使用Message對象給service發送命令。此外,客戶端能夠定義一個它本身的Messenger以使service能夠發送消息回去。
這是執行進程間通訊(IPC)最簡單的方式,由於Messenger把全部的請求入隊到一個單獨的線程,所以你不須要爲線程安全而外設計你的service。
使用AIDL
AIDL (Android Interface Definition Language)執行全部將對象分解成操做系統能夠理解的元語的工做,並在進程之間處理它們來執行IPC。前面的一種技術,即便用一個Messenger,其實是以AIDL爲它的底層結構的。如上面提到的,Messenger在一個單獨的線程中建立一個全部的客戶端請求的隊列,以使service在某一時刻只接收一個請求。若是,然而,你想要你的service並行的處理多個請求,那麼你能夠直接使用AIDL。在這種狀況下,你的service必須具備多線程的能力,而且要被構建的是線程安全的。
要直接使用AIDL,你必須建立一個定義了編程接口的.aidl文件。Android SDK工具使用這個文件來產生一個實現了接口並處理IPC的抽象類,隨後你能夠在你的service中擴展這個抽象類。
注意:大多數應用不該該使用AIDL來建立一個bound service,由於它可能須要多線程能力,並可能致使一個更復雜的實現。所以,AIDL對於大多數應用都是不合適的,而且這份文檔中不討論如何使用它來實現你的service。若是你確認你須要直接使用AIDL,請參考AIDL文檔。
若是你的service只被本地應用用到了,而且不須要跨進程工做,那麼你能夠實現你本身的Binder類,來爲你的客戶端提供直接訪問service中的public方法的能力。
注意:這種方法只有在客戶端和service在相同的應用和進程中時纔有效,固然這種狀況很常見。例如,對於一個音樂應用,須要bind一個activity到它本身的在後臺播放音樂的service,就頗有效。
這裏是如何設置它:
在你的service中建立一個下述類型之一的Binder實例:
(1). 包含了客戶端能夠調用的public方法
(2). 返回當前的Service實例,其中具備客戶端能夠調用的public方法
(3). 或者,返回另外一個類的實例,其中寄宿了service,具備客戶端能夠調用的public方法。
在客戶端中,從onServiceConnected()回調方法接收這個Binder,並使用提供的方法來調用bound service。
注意:service和客戶端必須位於相同的應用中的理由是,這樣客戶端能夠強制類型轉換返回的對象,並適當地調用它的APIs。service和客戶端必須處於相同的進程,因爲這項技術不執行任何的跨進程處理。
好比,這兒有一個service,它經過一個Binder實現,提供客戶端訪問service中的方法:
public class LocalService extends Service { // Binder given to clients private final IBinder mBinder = new LocalBinder(); // Random number generator private final Random mGenerator = new Random(); /** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */ public class LocalBinder extends Binder { LocalService getService() { // Return this instance of LocalService so clients can call public methods return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** method for clients */ public int getRandomNumber() { return mGenerator.nextInt(100); } }
LocalBinder爲客戶端提供了getService()來獲取當前的LocalService實例。這將容許客戶端調用service中的public方法。好比,客戶端能夠調用service的getRandomNumber()。
這裏是一個activity,它bind到LocalService,並在button被點擊時調用getRandomNumber():
public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) */ public void onButtonClick(View v) { if (mBound) { // Call a method from the LocalService. // However, if this call were something that might hang, then this request should // occur in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /** Defines callbacks for service binding, passed to bindService() */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }
上面的例子演示了客戶端如何使用一個ServiceConnection實現和onServiceConnected()回調bind到service。下一節將提供更多關於binding到service的這一過程的信息。
注意:上面的例子沒有顯式地從service unbind,但全部的客戶端都應該在一個適當的時間點 (好比當activity pause時) unbind。
.更多示例代碼,請參考ApiDemos中的LocalService.java類和LocalServiceActivities.java類。
當你須要執行IPC時,使用一個Messenger來實現你的接口比經過AIDL來實現它要簡單,由於Messenger將全部的調用入隊到service,然而,一個純粹的AIDL接口發送併發的請求給service,這將必需要處理多線程。
對於大多數應用,service不須要執行多線程,所以使用一個Messenger容許service每次只處理一個調用。若是你的service必定要是多線程的,那麼你應該使用AIDL來定義你的接口。
若是你須要你的service與遠端進程通訊,那麼你可使用一個Messenger來爲你的service提供接口。這項技術容許你在不須要使用AIDL的狀況下執行進程間通訊(IPC)。
這裏是一個如何使用一個Messenger的總結:
service實現一個Handler,它爲來自於客戶端的每一個調用接收一個回調。
客戶端使用IBinder來實例化Messenger (引用了service的Handler),其可被客戶端用來給service發送Message發對象。
service在它的Handler中接收每一個Message——典型地,在handleMessage()方法中。
以這種方式,客戶端不調用service的"方法"。而是,客戶端傳送"消息" (Message 對象),service在它的Handler中接收消息。
這裏有一個簡單的使用了Messenger接口的示例service:
public class MessengerService extends Service { /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } }
注意,Handler中的handleMessage()方法正是service接收傳入的Message並根據what成員決定作什麼的地方。
一個客戶端所須要作的就是建立一個基於service返回的IBinder的Messenger,並使用send()來發送一個消息。好比,這裏是一個簡單的activity,它binds到service,並傳送MSG_SAY_HELLO消息給service:
public class ActivityMessenger extends Activity { /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // Create and send a message to the service, using a supported 'what' value Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to the service bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } }
這個例子沒有演示service能夠如何響應客戶端。若是你想要service響應,則你也須要在客戶端建立一個Messenger。而後當客戶端接收到onServiceConnected()回調時,它發送一個Message 給service,其中在send()方法的replyTo參數中包含了客戶端的Messenger。
你能夠參考示例MessengerService.java (service)和MessengerServiceActivities.java (client)來看一下如何提供兩路消息的例子。
應用組件(客戶端)能夠經過調用bindService()來bind到一個service。而後,Android系統調用service的onBind()方法,它會返回一個IBinder用於與service交互。
binding是異步的。bindService()會當即返回,而不給客戶端返回IBinder。要接收IBinder,客戶端必須建立一個ServiceConnection的實例,並把它傳遞給bindService()。ServiceConnection包含一個回調方法,系統能夠調用它來傳送IBinder。
注意:只有activities,services,和content providers能夠bind到一個service——你不能在一個broadcast receiver中bind到一個service。
於是,要在客戶端中bind到一個series,你必須:
你的實現必須覆寫兩個回調方法:
系統調用這個方法來傳送service的onBind()方法返回的IBinder。
當與service的鏈接意外斷開時,Android系統會調用它。好比當service發生了crash或已 經被殺掉時。當client unbinds時則不會被調到。
調用bindService(),並傳入ServiceConnection的實現。
當系統調用了你的onServiceConnected()回調時,你就能夠開始調用service了,使用接口所定義的方法。
要從service斷開鏈接,則調用unbindService()。
當你的客戶端被銷燬時,它將從service unbind,但你應該老是在你完成了與service的交互或者你的activity pause時unbind,以使得service能夠在它沒有被用到時關閉。(下面有更多關於bind和unbind的合適的時間的討論。)
例如,下面的代碼片斷把客戶端與上面經過擴展Binder類建立的service鏈接起來,它所必須作的一切就是把返回的IBinder強制類型轉換到LocalService類,請向LocalService實例發起請求:
LocalService mService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Because we have bound to an explicit // service that is running in our own process, we can // cast its IBinder to a concrete class and directly access it. LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "onServiceDisconnected"); mBound = false; } };
藉助於這個ServiceConnection,客戶端能夠經過把它傳遞給bindService()來bind到一個service。好比:
Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService()的第一個參數是一個Intent,它顯式地指定了要bind的service的名字(intent也能夠是隱式的)。
第二個參數是ServiceConnection對象。
第三個參數是一個表示binding選項的標記。它一般應該是BIND_AUTO_CREATE,以使service在沒有存活的狀況下被建立出來。其它的可用的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或0以表示none。
這裏是關於binding到一個service的一些重要的說明:
你應該老是捕獲DeadObjectException一場,當鏈接破壞時它會被拋出。這是遠程方法拋出的僅有的異常。
對象是在進程間引用計數的。
你一般應該在客戶端的生命週期中對應的bring-up和tear-down的時刻成對的調用binding和unbinding。好比:
若是你只須要在你的activity可見時與service交互,則你應該在onStart()中bind,並在onStop()中unbind。
若是你想要你的activity即便已經被stopped了,也要在後臺接收響應,則你能夠在onCreate()中bind,並在onDestroy()中unbind。注意,這意味着你的activity須要在它運行的全部時間都使用service(即便是在後臺),所以若是service在另外一個進程,則你將增長那個進程的weight,而且它變得更可能被系統殺掉。
注意:一般你不該該在你的activity的onResume()和onPause()中bind和unbind,由於這些回調在每個生命週期事務中都會發生,你應該把這些事務中發生的處理量降到最低。若是你的應用中的多個activities bind到相同的service,而且在這些activities中的兩個之間有一個事務,則在當前的activity unbinds (在pause中)以後,下一個binds (在resume中) 以前可能會發生service的銷燬和重建。(這種activities如何協調他們的生命週期的activity事務,在Activities文檔中有描述)。
更多演示如何bind到一個service的示例代碼,能夠參考ApiDemos中的RemoteService.java類。
當全部的客戶端都從service unbound時,Android系統會銷燬它(除非它也同時經過onStartCommand()被started了)。所以,你不須要管理你的service的生命週期,若是它是純粹的bound service的話——Android系統會基於它是否被bound到了客戶端來爲你管理它。
然而,若是你選擇實現onStartCommand()回調方法,則你須要顯式的來中止service,由於如今service被認爲是被started的了。在這種狀況下,service會一直運行,直到service經過stopSelf()來終止它本身,或者另外一個組件調用stopService(),而無論它是否被bound到任何的客戶端。
此外,若是你的service是被started的,並接受binding,那麼當系統調用了你的onUnbind()方法,若是你想要在下次客戶端binds到service時接收一個對onRebind()的調用(而不是接收一個對onBind()的調用),你能夠選擇返回true。onRebind()返回void,可是客戶端仍然在它的onServiceConnected()回調中接收IBinder。下面的圖1描繪了這種生命週期的邏輯。
圖1. The lifecycle for a service that is started and also allows binding.
更多關於一個started的service的生命週期的信息,請參考Services文檔。
Done。