bound服務(wù)是客戶端-服務(wù)器模式的服務(wù)。bound服務(wù)允許組件(比如activity)對(duì)其進(jìn)行綁定、發(fā)送請(qǐng)求、接收響應(yīng)、甚至進(jìn)行進(jìn)程間通信(IPC)。 bound服務(wù)一般只在為其它應(yīng)用程序組件服務(wù)期間才是存活的,而不會(huì)一直在后臺(tái)保持運(yùn)行。 本文展示了如何創(chuàng)建一個(gè)bound服務(wù),包括如何從其它應(yīng)用程序組件綁定到該服務(wù)。不過(guò),通常你還應(yīng)該參考 Services 文檔以獲取關(guān)于服務(wù)的更多信息,比如如何從服務(wù)中發(fā)送通知、如何將服務(wù)設(shè)置為前臺(tái)運(yùn)行等等。 簡(jiǎn)介綁定到一個(gè)started服務(wù) 正如 Services 一文中所述,你可以創(chuàng)建一個(gè)同時(shí)支持started和bound的服務(wù)。也就是說(shuō),服務(wù)可以通過(guò)調(diào)用 startService() 來(lái)啟動(dòng),這使它一直保持運(yùn)行,同時(shí)它也允許客戶端通過(guò)調(diào)用 bindService() 來(lái)與之綁定。 如果你的服務(wù)確實(shí)可以是started和bound的,那么服務(wù)啟動(dòng)后,系統(tǒng)將不會(huì)在所有客戶端解除綁定時(shí)銷毀它。取而代之的是,你必須通過(guò)調(diào)用 stopSelf() 或 stopService() 顯式終止此服務(wù)。 雖然你通常應(yīng)該要實(shí)現(xiàn) onBind() 或 onStartCommand() 中的一個(gè),但有時(shí)需要同時(shí)實(shí)現(xiàn)兩者。 比如,音樂(lè)播放器的服務(wù)也許就需要同時(shí)實(shí)現(xiàn)后臺(tái)運(yùn)行和支持綁定。 這樣,activity就可以啟動(dòng)服務(wù)來(lái)播放音樂(lè),并且音樂(lè)會(huì)一直播放下去,即使用戶離開該應(yīng)用程序也沒關(guān)系,這個(gè)activity可以綁定播放服務(wù)來(lái)重新獲得播放控制權(quán)。 請(qǐng)確保已經(jīng)閱讀了 管理Bound服務(wù)的生命周期 章節(jié),以獲取更多向started服務(wù)添加綁定時(shí)的服務(wù)生命周期的有關(guān)信息。 bound服務(wù)是 Service 類的一種實(shí)現(xiàn),它允許其它應(yīng)用程序與其綁定并交互。為了讓服務(wù)支持綁定,你必須實(shí)現(xiàn) onBind() 回調(diào)方法。這個(gè)方法返回一個(gè) IBinder 對(duì)象,此對(duì)象定義了客戶端與服務(wù)進(jìn)行交互時(shí)所需的編程接口。 客戶端可以通過(guò)調(diào)用 bindService() 方法來(lái)綁定服務(wù)。在調(diào)用時(shí),必須提供一個(gè) ServiceConnection 的實(shí)現(xiàn)代碼,用于監(jiān)控與服務(wù)的聯(lián)接。 bindService() 將會(huì)立即返回,沒有返回值。但是Android系統(tǒng)在創(chuàng)建客戶端與服務(wù)之間的聯(lián)接時(shí),會(huì)調(diào)用 ServiceConnection 中的 onServiceConnected() 方法,傳遞一個(gè) IBinder ,客戶端將用它與服務(wù)進(jìn)行通信。 多個(gè)客戶端可以同時(shí)聯(lián)接到一個(gè)服務(wù)上。不過(guò),只有在第一個(gè)客戶端綁定時(shí),系統(tǒng)才會(huì)調(diào)用服務(wù)的 onBind() 方法來(lái)獲取 IBinder 。然后,系統(tǒng)會(huì)向后續(xù)請(qǐng)求綁定的客戶端傳送這同一個(gè) IBinder ,而不再調(diào)用 onBind() 。 當(dāng)最后一個(gè)客戶端解除綁定后,系統(tǒng)會(huì)銷毀服務(wù)(除非服務(wù)同時(shí)是通過(guò) startService() 啟動(dòng)的)。 當(dāng)你實(shí)現(xiàn)自己的bound服務(wù)時(shí),最重要的工作就是定義 onBind() 回調(diào)方法所返回的接口。定義服務(wù) IBinder 接口的方式有好幾種,后續(xù)章節(jié)將會(huì)對(duì)每種技術(shù)進(jìn)行 創(chuàng)建一個(gè)支持綁定的服務(wù)時(shí),你必須提供一個(gè) IBinder ,用作客戶端和服務(wù)間進(jìn)行通信的編程接口。定義這類接口的方式有三種: 1、擴(kuò)展Binder類 如果服務(wù)是你的應(yīng)用程序所私有的,并且與客戶端運(yùn)行于同一個(gè)進(jìn)程中(通常都是如此),你應(yīng)該通過(guò)擴(kuò)展 Binder 類來(lái)創(chuàng)建你的接口,并從 onBind() 返回一個(gè)它的實(shí)例??蛻舳私邮赵?Binder 對(duì)象并用它來(lái)直接訪問(wèn) Binder 甚至 Service 中可用的公共(public)方法。 如果你的服務(wù)只是為你自己的應(yīng)用程序執(zhí)行一些后臺(tái)工作,那這就是首選的技術(shù)方案。不用這種方式來(lái)創(chuàng)建接口的理由只有一個(gè),就是服務(wù)要被其它應(yīng)用程序使用或者要跨多個(gè)進(jìn)程使用。 2、使用Messenger 如果你需要接口跨越多個(gè)進(jìn)程進(jìn)行工作,可以通過(guò) Messenger 來(lái)為服務(wù)創(chuàng)建接口。在這種方式下,服務(wù)定義一個(gè)響應(yīng)各類消息對(duì)象 Message 的 Handler 。此 Handler 是 Messenger 與客戶端共享同一個(gè) IBinder 的基礎(chǔ),它使得客戶端可以用消息對(duì)象 Message 向服務(wù)發(fā)送指令。此外,客戶端還可以定義自己的 Message ,以便服務(wù)能夠往回發(fā)送消息。 這是執(zhí)行進(jìn)程間通信(IPC)最為簡(jiǎn)便的方式,因?yàn)?Messenger 會(huì)把所有的請(qǐng)求放入一個(gè)獨(dú)立進(jìn)程中的隊(duì)列,這樣你就不一定非要把服務(wù)設(shè)計(jì)為線程安全的模式了。 3、使用AIDL Android接口定義語(yǔ)言AIDL(Android Interface Definition Language) 完成以下的所有工作:將對(duì)象解析為操作系統(tǒng)可識(shí)別的原始形態(tài),并將它們跨進(jìn)程序列化(marshal)以完成IPC。 前一個(gè)使用 Messenger 的方式,實(shí)際上也是基于AIDL的,它用AIDL作為底層結(jié)構(gòu)。如上所述, Messenger 將在一個(gè)單獨(dú)的進(jìn)程中創(chuàng)建一個(gè)包含了所有客戶端請(qǐng)求的隊(duì)列,這樣服務(wù)每次就只會(huì)收到一個(gè)請(qǐng)求。 可是,如果想讓你的服務(wù)能同時(shí)處理多個(gè)請(qǐng)求,那你就可以直接使用AIDL。 這種情況下,你的服務(wù)必須擁有多線程處理能力,并且是以線程安全的方式編寫的。 要直接使用AIDL,你必須創(chuàng)建一個(gè).aidl文件,其中定義了編程的接口。 Android SDK 工具使用此文件來(lái)生成一個(gè)抽象類(abstract class),其中實(shí)現(xiàn)了接口及對(duì)IPC的處理,然后你就可以在自己的服務(wù)中擴(kuò)展該類。 注意: 絕大多數(shù)應(yīng)用程序都不應(yīng)該用AIDL來(lái)創(chuàng)建bound服務(wù),因?yàn)檫@可能需要多線程處理能力并且會(huì)讓代碼變得更為復(fù)雜。 因此,AIDL對(duì)絕大多數(shù)應(yīng)用程序都不適用,并且本文也不會(huì)討論如何在服務(wù)中使用它的內(nèi)容。如果你確信需要直接使用AIDL,那請(qǐng)參閱 AIDL 文檔。 擴(kuò)展Binder類如果你的服務(wù)只用于本地應(yīng)用程序并且不需要跨進(jìn)程工作,那你只要實(shí)現(xiàn)自己的 Binder 類即可,這樣你的客戶端就能直接訪問(wèn)服務(wù)中的公共方法了。 注意: 僅當(dāng)客戶端和服務(wù)位于同一個(gè)應(yīng)用程序和進(jìn)程中,這也是最常見的情況,這種方式才會(huì)有用。 比如,一個(gè)音樂(lè)應(yīng)用需要把一個(gè)activity綁定到它自己的后臺(tái)音樂(lè)播放服務(wù)上,采用這種方式就會(huì)很不錯(cuò)。 以下是設(shè)置步驟:
注意: 服務(wù)和客戶端之所以必須位于同一個(gè)應(yīng)用程序中,是為了讓客戶端能夠正確轉(zhuǎn)換(cast)返回的對(duì)象并調(diào)用對(duì)象的API。 服務(wù)和客戶端也必須位于同一個(gè)進(jìn)程中,因?yàn)檫@種方式不能執(zhí)行任何跨進(jìn)程的序列化(marshalling)操作。 比如,以下是一個(gè)服務(wù)的示例,它通過(guò)實(shí)現(xiàn)一個(gè) Binder 來(lái)為客戶端訪問(wèn)它內(nèi)部的方法提供支持: public class LocalService extends Service { // 給客戶端的Binder private final IBinder mBinder = new LocalBinder(); // 生成隨機(jī)數(shù) private final Random mGenerator = new Random(); 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; } public int getRandomNumber() { return mGenerator.nextInt(100); } } LocalBinder為客戶端提供了getService()方法,用于返回當(dāng)前LocalService的實(shí)例。 這就讓客戶端可以調(diào)用服務(wù)中的公共方法。比如,客戶端可以調(diào)用服務(wù)中的getRandomNumber()。 以下是一個(gè)綁定到LocalService的activity,當(dāng)點(diǎn)擊按鈕時(shí),它會(huì)調(diào)用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(); // 綁定到LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // 與服務(wù)解除綁定 if (mBound) { unbindService(mConnection); mBound = false; } } public void onButtonClick(View v) { if (mBound) { // 調(diào)用LocalService中的方法。 // 不過(guò),如果該調(diào)用會(huì)導(dǎo)致某些操作的掛起,那么調(diào)用應(yīng)該放入單獨(dú)的線程中進(jìn)行, // 以免降低activity的性能。 int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // 我們已經(jīng)綁定到LocalService了,對(duì)IBinder進(jìn)行類型轉(zhuǎn)換(cast)并獲得LocalService對(duì)象的實(shí)例 LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; } 上述例子展示了客戶端如何利用 ServiceConnection 和 onServiceConnected() 回調(diào)方法綁定到服務(wù)。下一節(jié)將給出更多有關(guān)服務(wù)綁定過(guò)程的信息。 注意: 上述例子并沒有明確地解除綁定,但所有的客戶端都應(yīng)該適時(shí)地解除綁定(比如activity暫停pause時(shí))。 更多示例代碼,請(qǐng)參閱 ApiDemos 中的 LocalService.java 類和 LocalServiceActivities.java 類。 使用Messenger與AIDL相比 當(dāng)你需要進(jìn)行IPC時(shí),使用 Messenger 要比用AIDL實(shí)現(xiàn)接口要容易些,因?yàn)?Messenger 會(huì)把所有調(diào)用服務(wù)的請(qǐng)求放入一個(gè)隊(duì)列。而純粹的AIDL接口會(huì)把這些請(qǐng)求同時(shí)發(fā)送給服務(wù),這樣服務(wù)就必須要能夠多線程運(yùn)行。 對(duì)于絕大多數(shù)應(yīng)用程序而言,服務(wù)沒有必要多線程運(yùn)行,因此利用 Messenger 可以讓服務(wù)一次只處理一個(gè)調(diào)用。如果 你的服務(wù)非要多線程運(yùn)行,那你就應(yīng)該用 AIDL 來(lái)定義接口。 如果你的服務(wù)需要與遠(yuǎn)程進(jìn)程進(jìn)行通信,那你可以使用一個(gè) Messenger 來(lái)提供服務(wù)的接口。這種技術(shù)能讓你無(wú)需使用AIDL就能進(jìn)行進(jìn)程間通信(IPC)。 以下概括了 Messenger 的使用方法:
通過(guò)這種方式,客戶端不需要調(diào)用服務(wù)中的“方法”。取而代之的是,客戶端發(fā)送“消息”( Message 對(duì)象),服務(wù)則接收位于 Handler 中的這個(gè)消息。 以下是服務(wù)使用一個(gè) Messenger 做為接口的簡(jiǎn)單例子: public class MessengerService extends Service { static final int MSG_SAY_HELLO = 1; 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); } } } final Messenger mMessenger = new Messenger(new IncomingHandler()); @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } } 請(qǐng)注意 Handler 中的 handleMessage() 方法,這里是服務(wù)接收輸入消息 Message 的地方,也是根據(jù) what 數(shù)字來(lái)決定要執(zhí)行什么操作的地方。 客戶端要做的全部工作就是根據(jù)服務(wù)返回的 IBinder 創(chuàng)建一個(gè) Messenger ,并用 send() 方法發(fā)送一個(gè)消息。例如,以下是一個(gè)activity示例,它綁定到上述服務(wù),并向服務(wù)發(fā)送 MSG_SAY_HELLO消息: public class ActivityMessenger extends Activity { Messenger mService = null; boolean mBound; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // 與服務(wù)建立聯(lián)接后將會(huì)調(diào)用本方法, // 給出用于和服務(wù)交互的對(duì)象。 // 我們將用一個(gè)Messenger來(lái)與服務(wù)進(jìn)行通信, // 因此這里我們獲取到一個(gè)原始IBinder對(duì)象的客戶端實(shí)例。 mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // 當(dāng)與服務(wù)的聯(lián)接被意外中斷時(shí)——也就是說(shuō)服務(wù)的進(jìn)程崩潰了, // 將會(huì)調(diào)用本方法。 mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // 創(chuàng)建并向服務(wù)發(fā)送一個(gè)消息,用到了已約定的'what'值 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(); // 綁定服務(wù) bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // 解綁服務(wù) if (mBound) { unbindService(mConnection); mBound = false; } } } 請(qǐng)注意,上述例子中沒有給出服務(wù)是如何響應(yīng)客戶端的。如果你需要服務(wù)進(jìn)行響應(yīng),那你還需要在客戶端創(chuàng)建一個(gè) Messenger 。然后,當(dāng)客戶端接收到 onServiceConnected() 回調(diào)后,它再發(fā)送一個(gè)消息 Message 給服務(wù),消息的 send() 方法中的 replyTo 參數(shù)里包含了客戶端的 Messenger 。 在 MessengerService.java (服務(wù))和 MessengerServiceActivities.java (客戶端)例程中,你可以看到如何雙向發(fā)送消息的例子。 綁定一個(gè)服務(wù)應(yīng)用程序組件(客戶端)可以通過(guò)調(diào)用 bindService() 來(lái)綁定服務(wù)。然后Android系統(tǒng)會(huì)調(diào)用服務(wù)的 onBind() 方法,返回一個(gè)用于和服務(wù)進(jìn)行交互的 IBinder 。 綁定是異步進(jìn)行的。 bindService() 將立即返回,并不會(huì)向客戶端返回 IBinder 。為了接收 IBinder ,客戶端必須創(chuàng)建一個(gè) ServiceConnection 的實(shí)例,并把它傳給 bindService() 。 ServiceConnection 包含了一個(gè)回調(diào)方法,系統(tǒng)將會(huì)調(diào)用該方法來(lái)傳遞客戶端所需的那個(gè) IBinder 。 注意: 只有activity、服務(wù)和content provider才可以綁定到服務(wù)上——你不能從廣播接收器(broadcast receiver)中綁定服務(wù)。 因此,要把客戶端綁定到服務(wù)上,你必須:
當(dāng)客戶端被銷毀時(shí),與服務(wù)的綁定也將解除。但與服務(wù)交互完畢后,或者你的activity進(jìn)入pause狀態(tài)時(shí),你都應(yīng)該確保解除綁定,以便服務(wù)能夠在用完后及時(shí)關(guān)閉。 (綁定和解除綁定的合適時(shí)機(jī)將在后續(xù)章節(jié)中繼續(xù)討論。) 例如,以下代碼段將客戶端與前面 擴(kuò)展Binder類 創(chuàng)建的服務(wù)聯(lián)接,而要做的全部工作就是把返回的 IBinder 轉(zhuǎn)換(cast)為L(zhǎng)ocalService類,并獲取LocalService的實(shí)例: LocalService mService; private ServiceConnection mConnection = new ServiceConnection() { // 與服務(wù)的聯(lián)接建立之后將會(huì)調(diào)用 public void onServiceConnected(ComponentName className, IBinder service) { // 因?yàn)槲覀円呀?jīng)與明顯是運(yùn)行于同一進(jìn)程中的服務(wù)建立了聯(lián)接, // 我們就可以把它的IBinder轉(zhuǎn)換為一個(gè)實(shí)體類并直接訪問(wèn)它。 LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } // 與服務(wù)的聯(lián)接意外中斷時(shí)將會(huì)調(diào)用 public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "onServiceDisconnected"); mBound = false; } }; 利用這個(gè) ServiceConnection ,客戶端就能夠把它傳入 bindService() 完成與服務(wù)的綁定。例如: Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
其它注意事項(xiàng)以下是有關(guān)綁定服務(wù)的一些重要注意事項(xiàng):
注意: 你通常不應(yīng)該在activity的 onResume() 和 onPause() 中綁定和解除綁定,因?yàn)檫@兩個(gè)回調(diào)方法在每次切換生命周期狀態(tài)時(shí)都會(huì)發(fā)生,這時(shí)你應(yīng)該讓處理工作最少化。 而且,如果應(yīng)用程序中有多個(gè)activity都綁定到同一個(gè)服務(wù)上,則在兩個(gè)activity間切換時(shí)都會(huì)發(fā)生狀態(tài)轉(zhuǎn)換, 因?yàn)楫?dāng)前activity解除綁定(在pause時(shí))后,緊接著下一個(gè)activity又會(huì)進(jìn)行綁定(resume時(shí)),所以服務(wù)也許在銷毀后馬上就要重建。 (這種activity狀態(tài)轉(zhuǎn)換、多個(gè)activity間的生命周期協(xié)作在 Activities 文檔中描述。) 更多展示綁定服務(wù)的示例代碼,請(qǐng)參閱 piDemos 中的 RemoteService.java 類。 管理Bound服務(wù)的生命周期
一旦服務(wù)被所有客戶端解除綁定,則Android系統(tǒng)將會(huì)銷毀它(除非它同時(shí)又是用 onStartCommand() started)。因此,如果你的服務(wù)就是一個(gè)純粹的bound服務(wù),那你就不需要管理它的生命周期——Android系統(tǒng)會(huì)替你管理,根據(jù)是否還有客戶端對(duì)其綁定即可。 不過(guò),如果你選擇實(shí)現(xiàn) onStartCommand() 回調(diào)方法,那么你就必須顯式地終止服務(wù),因?yàn)榇朔?wù)現(xiàn)在已經(jīng)被視為started了。這種情況下,無(wú)論是否還存在客戶端與其綁定,此服務(wù)都會(huì)運(yùn)行下去,直至自行用 stopSelf() 終止或由其它組件調(diào)用 stopService() 來(lái)終止。 此外,如果你的服務(wù)是started且允許被綁定,那么系統(tǒng)調(diào)用你的 onUnbind() 方法時(shí),你可以選擇返回true。這樣作的結(jié)果就是,下次客戶端綁定時(shí)將會(huì)收到 onRebind() 調(diào)用(而不是收到 onBind() 調(diào)用)。 onRebind() 返回void,但客戶端仍然能在它的 onServiceConnected() 回調(diào)方法中收到 IBinder 。圖1展示了這種生命周期的運(yùn)行邏輯。 關(guān)于started服務(wù)生命周期的更多信息,請(qǐng)參閱 Services 文檔 。 |
|
來(lái)自: lifei_szdz > 《service》