Service中的绑定服务总结
綁定服務是客戶端服務器接口中的服務器,綁定服務可以讓組件綁定到服務、發送請求、接收響應,甚至執行進程間通信IPC,綁定服務通常只在為其他應用組件服務時處于活動狀態,不會無限期在后臺運行。
綁定服務是Service類的實現,必須實現onBind()回調方法,返回的IBinder對象定義了客戶端用來與服務進行交互的編程接口。客戶端通過調用bindService()綁定到服務,調用時,必須提供ServiceConnection的實現,bindService()會立即無值返回,當系統創建客戶端與服務之間的連接時,會對ServiceConnection調用onServiceConnected(),向客戶端傳遞用來與服務通信的IBinder。
多個客戶端可以連接到一個服務,只有第一個客戶端綁定服務時,系統才會調用onBind()來檢索IBinder,隨后無需再調用onBind(),可將同一IBinder傳遞至其他綁定的客戶端。當最后一個客戶端取消綁定時,系統會將服務銷毀。實現綁定服務時,最重要的是定義onBind()回調方法返回接口,可以通過幾種不同方法定義服務的IBinder接口。
創建綁定服務
創建綁定服務時,必須提供IBinder,用以提供客戶端用來與服務進行交互的接口,可以通過三種方法定義接口。
擴展Binder類---若服務是該應用專用且與客戶端相同的進程中運行,則應通過擴展Binder類從onBind()返回一個實例來創建接口,客戶端收到Binder后,直接訪問Binder實現中乃至Service中公共方法。若服務只是自有應用的后臺工作線程,應優先使用這種辦法。
使用Messenger---如需讓接口跨不同的進程工作,則可以使用Messenger為服務創建接口,以這種方式定義的服務可以對應于不同類型Message對象的Handler,此Handler是Messenger的基礎,Messenger隨后可與客戶端分享一個IBinder,從而讓客戶端利用Message對象向服務發送命令,此外,客戶端還可定義自有Messenger,以便服務回傳消息。這是執行進程間通信IPC最簡單的方法,因為Messenger會在單一線程中創建包含所有請求的隊列,這樣不必對服務進行線程安全設計。
使用AIDL---Android接口定義語言,執行所有將對象分解成原語的工作,操作系統可以識別這些原語并將其編組到各進程中,以執行IPC。采用Messenger的方法實際是以AIDL作為其底層結構。Messenger在單一線程中創建包含所有客戶端請求的隊列,服務一次接收一個請求。若想讓服務同時處理多個請求,則使用AIDL,此時,必須創建一個定義編程接口.aidl文件,AndroidSDK工具利用該文件生成一個實現接口并處理IPC的抽象,隨后便可以在服務內進行擴展,注意,服務必須具備處理多線程的能力,并采用線程安全式的設計。AIDL不常用,因為比較復雜。
擴展Binder類:
若服務僅供本地應用使用,不需要跨進程工作,則最好實現自有Binder類,讓客戶端通過該類直接訪問服務中的公共方法。此方法只有在客戶端和服務處于同一應用和進程中才有效,如對于需要將Activity綁定到在后臺播放音樂的自有服務的音樂應用。
1.創一個可滿足一下全部Binder實例:
包含客戶端可調用的公共方法。
返回當前Service實例
返回由服務承載的其他類的實例
2.從onBind()回調方法返回此Binder實例。
3.在客戶端中,從onServiceConnected()回調方法接收Binder,并使用提供的方法調用綁定服務。
如,以下示例服務可讓客戶端通過Binder實現訪問服務中的方法:
public class LocalService extends Service {// Binder被給向客戶端private final IBinder mBinder = new LocalBinder();// 生成隨機數private final Random mGenerator = new Random();//專為客戶端使用的類,由于此服務與客戶端總是在同一進程中public class LocalBinder extends Binder {LocalService getService() {//返回this以便LocalService可以調用公共方法return LocalService.this;}}@Overridepublic IBinder onBind(Intent intent) {return mBinder;}//客戶端的方法public int getRandomNumber() {return mGenerator.nextInt(100);} }LocalBinder為客戶端提供getService()方法,以檢索LocalService的當前實例,客戶端便可調用服務中的公共方法。點擊按鈕時,以下Activity便會綁定到LocalService并調用getRandomNumber()方法
public class BindingActivity extends Activity {LocalService mService;boolean mBound = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotected void onStart() {super.onStart();//綁定到LocalServiceIntent intent = new Intent(this, LocalService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {super.onStop();// 將客戶端與服務取消綁定,客戶端應在適當時機與服務取消綁定if (mBound) {unbindService(mConnection);mBound = false;}}//當點擊按鈕時,調用此方法public void onButtonClick(View v) {if (mBound) {// 從LocalService調用一個方法,但是如果這個調用是可能被掛起的,那么這次請求將會放置在其他的線程中以防止Activity減緩int num = mService.getRandomNumber();Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();}}//定義綁定服務的返回值,傳至bindService()private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName className,IBinder service) {// 已經綁定至LocalService,轉換IBinder并獲取LocalService實例LocalBinder binder = (LocalBinder) service;mService = binder.getService();mBound = true;}@Overridepublic void onServiceDisconnected(ComponentName arg0) {mBound = false;}}; } 使用Messenger如需讓服務與遠程進程通信,可使用Messenger為服務提供接口。無需使用AIDL便可執行進程間通信。
使用Messenger的方法:
服務實現一個Handler,由其接受來自客戶端的每個調用的回調。
Handler用于創建Messenger對象
Messenger創建一個IBinder,服務通過onBind()使其返回客戶端
客戶端使用IBinder將Messenger實例化,然后使用Messenger將Message對象發給服務
這樣客戶端沒有調用服務的“方法”,客戶端傳遞的“消息”(Message對象)是服務在Handler中接收的。
如下:
public class MessengerService extends Service {//要求服務顯示一個message對象static final int MSG_SAY_HELLO = 1;//來自客戶端message的Handlerclass IncomingHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_SAY_HELLO:Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();break;default:super.handleMessage(msg);}}}//給客戶端發送message到IncomingHandlerfinal Messenger mMessenger = new Messenger(new IncomingHandler());//當綁定到服務,返回一個接口到messenger,用于發送message對象到服務@Overridepublic IBinder onBind(Intent intent) {Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();return mMessenger.getBinder();} }服務就是在Handler的handleMessage()方法中接受傳入的Message,并根據what成員決定下一步操作。客戶端只需根據服務返回IBinder創建一個Messenger,然后利用send()發送消息。
public class ActivityMessenger extends Activity {//聲明與服務聯系的MessengerMessenger mService = null;//標記是否已經綁定到服務boolean mBound;//與服務的主接口相互作用的類private ServiceConnection mConnection = new ServiceConnection() {public void onServiceConnected(ComponentName className, IBinder service) {//當服務的連接已經被建立時,給出可以與服務連接的對象,使用Messenger與服務聯系,所以得到一個從客戶端IBinder對象的代表、mService = new Messenger(service);mBound = true;}public void onServiceDisconnected(ComponentName className) {// 當與服務的連接意外斷開時調用,即線程崩潰mService = null;mBound = false;}};public void sayHello(View v) {if (!mBound) return;//創建并發送一個’what‘值的message到服務Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);try {mService.send(msg);} catch (RemoteException e) {e.printStackTrace();}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);}@Overrideprotected void onStart() {super.onStart();//與服務綁定bindService(new Intent(this, MessengerService.class), mConnection,Context.BIND_AUTO_CREATE);}@Overrideprotected void onStop() {super.onStop();//與服務取消綁定if (mBound) {unbindService(mConnection);mBound = false;}} }若想服務對客戶端做出響應,還需在客戶端中創建一個Messenger,然后當客戶端收到onServiceConnected()回調時,想服務發送一條Message,并在其send()方法的replyTo參數中包含客戶端的Messenger綁定到服務
客戶端可以通過調用bindService()綁定到服務,Android系統隨后調用服務的onBind()方法,用于返回用于與服務交互的IBinder。綁定是異步的,bindService()會立即返回,不會使IBinder返回客戶端,要接收IBinder,客戶端必須創建一個ServiceConnection實例,并將其傳遞給bindService,ServiceConnection包括一個回調方法,系統通過該方法來傳遞IBinder。
注,只有Activity、Service和內容提供程序可以綁定到服務,Broadcast不能綁定到服務
故,若想從客戶端綁定到服務,必須滿足以下條件:
1.實現ServiceConnection,必須重寫兩個回調方法
onServiceConnected()---系統會調用該方法以傳遞服務的onBind()方法返回的IBinder
onServiceDisconnected()---Android系統會在與服務連接意外中斷時(服務崩潰或終止時)調用該方法,當客戶端取消綁定時,系統不會調用該方法。
2.調用bindService(),傳遞ServiceConnection實現。
3.當系統調用onServiceConnected()回調方法時,可以使用接口定義的方法開始調用服務
4.調用unbindService()斷開與服務的連接。最好是在客戶端與服務交互完成后立即取消綁定客戶端,這樣可以關閉空閑服務。
以下例子通過擴展Binder類將客戶端與服務相連,只需將返回的IBinder轉換成LocalService類并請求LocalService實例:
LocalService mService; private ServiceConnection mConnection = new ServiceConnection() {//當與服務的連接被建立時調用public void onServiceConnected(ComponentName className, IBinder service) {// 由于已經與確定的本地線程中的服務綁定,能將其IBinder轉換為具體類并直接訪問LocalBinder binder = (LocalBinder) service;mService = binder.getService();mBound = true;}//當與服務的連接意外斷開時調用public void onServiceDisconnected(ComponentName className) {Log.e(TAG, "onServiceDisconnected");mBound = false;} };客戶端可通過將ServiceConnection傳遞至bindService()綁定到服務: Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);bindService()第一個參數是Intent,用于顯示命名要綁定的服務,第二個參數是ServiceConnection對象,第三個參數是一個指示綁定選項的標志,通常應該是BIND_AUTO_CREATE,以便創建尚未激活的服務。還可以是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND或0需要注意
應該始終捕獲DeadObjectException異常,是在連接中斷時引發的,是遠程方法引發的唯一異常。
對象是跨進程計數的引用
通常應該在客戶端生命周期的匹配引入和退出時刻配對綁定和解除綁定。一般Activity的onCreate()和onDestroy(),或者onCreate()和onDestroy()期間綁定和取消綁定,切勿在onResume()和onPause()綁定。
管理綁定服務的聲明周期
如果服務是純粹的綁定服務,無需對其生命周期進行管理。如果選擇實現onStartCommand()回調方法,必須顯示停止服務,服務將會一直運行到stopSelf()停止,或其他組件調用stopService()停止,無論其是否綁定到任何客戶端。若服務已經啟動且接受綁定,則系統調用onUnbind()方法時,若想在客戶端下次綁定到服務時接受onRebind()調用,則選擇返回true。onRebind()返回空值,但客戶端仍在其返回onServiceConnected()回調中接收IBinder。
總結
以上是生活随笔為你收集整理的Service中的绑定服务总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android服务部分总结
- 下一篇: Android内容提供程序