bindService簡介
Service一般用於不用顯示,運行在后台的服務。
startService 是我們最常用的啟動Service的方法。而如何讓service與其他組件通信呢?一般在一個進程內,可以使用廣播的方式讓Service與本進程其他Actvity/service進行通信,那么還有更好的方法嗎?如果要進行進程間通信(IPC)呢?
bindService就是解決這些問題的。
Binder通信機制介紹
在學習bindService之前,有必要對Binder通信機制有個基本的認識。為了提供系統全局服務,讓系統中的任何應用程序都能訪問這個全局服務,android設計了這個叫做Binder的client/server的通信結構。
binder通信是一種client-server的通信結構,
1.從表面上來看,是client通過獲得一個server的代理接口(Binder),對server進行直接調用;
2.實際上,代理接口中定義的方法與server中定義的方法是一一對應的;
3.client調用某個代理接口中的方法時,代理接口的方法會將client傳遞的參數打包成為Parcel對象;
4.代理接口將該Parcel發送給內核中的binder driver.
5.server會讀取binder driver中的請求數據,如果是發送給自己的,解包Parcel對象,處理並將結果返回;
6.整個的調用過程是一個同步過程,在server處理的時候,client會block住。
對於開發者而言,Binder通信中有個問題需要解決:客戶端如何獲得服務端的Binder對象應用
Service類已經解決了這個問題
bindService和Binder機制
public boolean bindService(Intent service , ServiceConnection conn, int flags);
public interface ServiceConnection {
public void onServiceConnected(ComponetName name , IBinder service);
public void onServiceDisconnected(ComponentName name);
}
當客戶端請求Ams啟動某個Service后,該Service如果正常啟動,那么Ams就會遠程調用ActivityThread類中的ApplicationThread對象,調用的參數包含Service的Binder引用,然后在ApplicationThread中會回調bindService中的conn接口。這樣客戶端就可以在onServiceConnected()方法中將其參數Service保存為一個全局變量。
這樣,客戶端(一般是activity)就可以直接訪問Service中的public方法和屬性了。
BindService Basics
如果要使用BindService,Service必須要實現onBind()回調方法,onBind()返回IBinder對象,這個IBinder對象就是客戶端程序中onServiceConnected()方法所需要的參數IBinder。一個客戶端可以使用BinderService綁定到一個Service上,此時這個客戶端必須提供ServiceConnection的實現,這樣才能獲得從Service中返回的IBinder。
多個客戶端可以綁定到一個Service,但Service只在第一個客戶端BindService時才會調用onBind()。后續BindService的客戶端獲得的IBinder都是從Service第一次調用onBind()中返回的IBinder。
如果Service是bindService啟動的(不是startService),那么當最后一個客戶端unBindService(),Service將會destroy。
Android官方定義IBinder的幾種方式
擴展binder類-Extending the Binder class
如果Service不需要在多個進程間工作,那么你可以實現你自己的Binder類,讓客戶端(一般是Activity)可以直接調用Service的public方法。(注:在同一個應用程序中,Activity和Service都屬於UI線程)
- 注解:這個方法只有在客戶端和Service在同一個應用程序和線程中時才可行,這種情況很常見。例如,這個方法對於一個音樂應用程序將會非常有用,它需要綁定一個Activity到它自己的Service用來以后台播放音樂。
以下為如何設定這個binder類:
1.在你的Service中,創建一個Binder類的實例,實現以下功能之一:
-
- 包含客戶端可以調用的public方法
- 返回當前Service的實例,其包含了客戶端可以訪問的public方法
- 或返回這個Service包含的另一個類,並含有客戶端可以訪問的public方法
3.在客戶端,從onServiceConnected()回調方法接收這個Binder,調用bindService()。
- 注解:Service和客戶端必須在同一個應用程序中的原因是客戶端可以計算返回的對象並恰當的調用其APIs。服務和客戶端也必須在同一個線程的原因是這種技術不能執行線程間操作。
例如,以下為一個為客戶端提供了通過Binder實現接入服務中方法的服務范例:
1 public class LocalService extends Service { 2 // Binder given to clients 3 private final IBinder mBinder = new LocalBinder(); 4 // Random number generator 5 private final Random mGenerator = new Random(); /** 6 * Class used for the client Binder. Because we know this service always 7 * runs in the same process as its clients, we don't need to deal with IPC. 8 */ 9 public class LocalBinder extends Binder { 10 LocalService getService() { 11 // Return this instance of LocalService so clients can call public methods 12 return LocalService.this; 13 } 14 } 15 16 @Override 17 public IBinder onBind(Intent intent) { 18 return mBinder; 19 } /** method for clients */ 20 public int getRandomNumber() { 21 return mGenerator.nextInt(100); 22 } 23 }
LocalBinder為客戶端提供了getService()方法來取得當前的LocalService實例。這個允許客戶端調用服務中的公共方法。例如,客戶端可以從服務中調用getRandomNumber()。
以下為,當一個按鈕被點擊時,一個綁定到LocalService的活動並調用getRandomNumber()方法:
1 public class BindingActivity extends Activity { 2 LocalService mService; 3 boolean mBound = false; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.main); 9 } 10 11 @Override 12 protected void onStart() { 13 super.onStart(); 14 // Bind to LocalService 15 Intent intent = new Intent(this, LocalService.class); 16 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 17 } 18 19 @Override 20 protected void onStop() { 21 super.onStop(); 22 // Unbind from the service 23 if (mBound) { 24 unbindService(mConnection); 25 mBound = false; 26 } 27 } 28 29 /** Called when a button is clicked (the button in the layout file attaches to 30 * this method with the android:onClick attribute) */ 31 public void onButtonClick(View v) { 32 if (mBound) { 33 // Call a method from the LocalService. 34 // However, if this call were something that might hang, then this request should 35 // occur in a separate thread to avoid slowing down the activity performance. 36 int num = mService.getRandomNumber(); 37 Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); 38 } 39 } 40 41 /** Defines callbacks for service binding, passed to bindService() */ 42 private ServiceConnection mConnection = new ServiceConnection() { 43 44 @Override 45 public void onServiceConnected(ComponentName className, 46 IBinder service) { 47 // We've bound to LocalService, cast the IBinder and get LocalService instance 48 LocalBinder binder = (LocalBinder) service; 49 mService = binder.getService(); 50 mBound = true; 51 } 52 53 @Override 54 public void onServiceDisconnected(ComponentName arg0) { 55 mBound = false; 56 } 57 }; 58 }
使用一個消息傳遞器-Using a Messenger
如果你需要你的Service能夠與遠程進程通信,那么你可以使用一個Messenger為你的服務提供接口。這個方法允許你執行進程間通信(IPC)而不需要使用AIDL(當Service要處理多線程的客戶端請求時,AIDL更加適合)。
以下為怎么樣使用Messenger的總結:
- Service實現了一個Handler,用來接收每一次客戶端發來的請求,並根據請求內容做處理。
- Service通過它的Handler接收每一個Message——更確切的說,是在handleMessage()方法中接收。
通過這種方法,在Service沒有客戶端能調用的“方法”。而是,客戶傳遞“消息”(Message對象),同時服務在其Handler中接收。
以下為服務使用一個Messenger接口的簡單范例:
1 public class MessengerService extends Service { 2 /** Command to the service to display a message */ 3 static final int MSG_SAY_HELLO = 1; /** 4 * Handler of incoming messages from clients. 5 */ 6 class IncomingHandler extends Handler { 7 @Override 8 public void handleMessage(Message msg) { 9 switch (msg.what) { 10 case MSG_SAY_HELLO: 11 Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); 12 break; 13 default: 14 super.handleMessage(msg); 15 } 16 } 17 } 18 /** 19 * Target we publish for clients to send messages to IncomingHandler. 20 */ 21 final Messenger mMessenger = new Messenger(new IncomingHandler()); 22 23 /** 24 * When binding to the service, we return an interface to our messenger 25 * for sending messages to the service. 26 */ 27 @Override 28 public IBinder onBind(Intent intent) { 29 Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); 30 return mMessenger.getBinder(); 31 } 32 }
注意Handler中的handleMessage()方法,Service接收客戶端發來的Message,基於what成員,決定下一步的處理。
客戶端所需做的只是基於Service返回的IBinder創建一個Messenger並使用send()方法發送一條消息。例如,以下為一個簡單的Activity范例,其綁定到了Service,並向服務傳遞了MSG_SAY_HELLO消息:
1 public class ActivityMessenger extends Activity { 2 /** Messenger for communicating with the service. */ 3 Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ 4 boolean mBound; 5 /** 6 * Class for interacting with the main interface of the service. 7 */ 8 private ServiceConnection mConnection = new ServiceConnection() { 9 public void onServiceConnected(ComponentName className, IBinder service) { 10 // This is called when the connection with the service has been 11 // established, giving us the object we can use to 12 // interact with the service. We are communicating with the 13 // service using a Messenger, so here we get a client-side 14 // representation of that from the raw IBinder object. 15 mService = new Messenger(service); 16 mBound = true; 17 } 18 19 20 public void onServiceDisconnected(ComponentName className) { 21 // This is called when the connection with the service has been 22 // unexpectedly disconnected -- that is, its process crashed. 23 mService = null; 24 mBound = false; 25 } 26 }; 27 28 public void sayHello(View v) { 29 if (!mBound) return; 30 // Create and send a message to the service, using a supported 'what' value 31 Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); 32 try { 33 mService.send(msg); 34 } catch (RemoteException e) { 35 e.printStackTrace(); 36 } 37 } 38 39 @Override 40 protected void onCreate(Bundle savedInstanceState) { 41 super.onCreate(savedInstanceState); 42 setContentView(R.layout.main); 43 } 44 45 @Override 46 protected void onStart() { 47 super.onStart(); 48 // Bind to the service 49 bindService(new Intent(this, MessengerService.class), mConnection, 50 Context.BIND_AUTO_CREATE); 51 } 52 @Override 53 protected void onStop() { 54 super.onStop(); 55 // Unbind from the service 56 if (mBound) { 57 unbindService(mConnection); 58 mBound = false; 59 } 60 } 61 }
參考文章:
Android Service的綁定 基礎概念篇 http://www.cnblogs.com/mengdd/archive/2013/03/24/2979710.html