Service(七)
Bound Services
The Basics
Bound services實現了Service類別,並允許其他應用程式綁定它與它互動。service要提供binding,必須實現onBind()回呼方法,此方法會回傳IBinder物件,IBinder定義了客戶端與service之間互動的接口。
客戶端通過呼叫bindService()方法綁定service,當客戶端執行綁定後,客戶端必須提供ServiceConnection,監視與service的連結。bindService()方法會立即返回,但是當android系統產生客戶端與service之間的連結,系統會呼叫ServiceConnection上的onServiceConnected()方法來傳遞IBinder物件,使客戶端可以與service通訊。
許多客戶端可以同時與service連結,然而,系統只會在第一個客戶端綁定時呼叫你的service的onBind()方法來取得IBinder物件,系統接著直接傳送相同的IBinder物件給另外的客戶端,不會再次呼叫onBind()方法來取得IBinder物件。
當最後一個客戶端不再綁定service時,系統就會銷毀service。
當你實現bound service時,最重要的部分就是定義onBind()方法,有幾種方式能定義onBind()方法。Handler是Messenger的基礎,與客戶端分享IBninder(),允許客戶端用Messenge物件寄送指令給service,除此之外,客戶端也可以擁有自己的messenger,所以service能寄送messenge回去。
這是一個進程間互相溝通的簡單方式,因為messenger排所有的請求入單一的執行續中,所以你不需設計你的service為thread-safe。
Creating a Bound Service
當我們要實現一個service並且提供綁定的功能,你必須提供IBinder物件給客戶端作為與service互動的接口,有三種方式可以定義接口。
Extending the Binder class
如果你的service是私有的,只有自己的應用程式且在同一個進程作為客戶端,你應該藉著Binder類別來產生接口,且是從onBind()方法回傳接口,客戶端取得接口(Binder)後就可以直接使用不管是在Binder或者是service的public方法。
這樣的方式比較適合使用在當你的service作為你的應用程式的後台工作者時。如果你想要你的service被其他應用程式使用或者被不同進程存取,你就不應該使用這種方式來實現bound service。
Using a Messenger
如果你需要接口能存取其他進程,你能用Messenger來產生service的接口。在此方式,service必須定義Handler來回應不同型態的Messenger,
Using AIDL
(看不太懂android官網上的解釋,先跳過,之後再加上來)
Extending the Binder class
繼承binder類別建立bound service有三個步驟:
- 在service中,定義Binder:
- 包含客戶端可以呼叫的public方法。
- 或回傳當前的service,它有客戶端可以呼叫的方法。
- 或
- 從onBind()回乎方法回傳Binder物件。
- 在客戶端,從onServiceConnected()中取得Binder物件。
以下為一個service例子,它提供客戶端通過Binder呼叫它的方法。
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要綁定LocalService並當按鈕被點擊後呼叫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;
}
};
}
Using a Messenger
如果你需要你的service與遠端的進程通訊,你可以使用Messenger物件為你的service提供接口。此技術允許你執行進程間通訊卻無需AIDL。
以下總結如何使用Messenger:
- Service實作Handler物件來接收每一個從客戶端的呼叫。
- Handler用來產生Messenger物件(必須引用Handler類別)。
- Messenger產生新的IBinder物件,service從onBind()方法中回傳給客戶端。
- 客戶端使用IBinder傳送Messenge給service來實現Messenger。
- Service在它的Handler接收每個Messenge,特別是在handleMessenge()方法中。
以下有一個使用Messenge為接口的例子
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中的handleMessenege()方法是service是接收傳入messenege及決定要執行甚麼的動作地方。
所有客戶端需要做的是基於回傳自service的IBinder以及用send()方法寄送messenge創建Messenger。以下有一個簡單的activity去綁定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()的回呼時,它寄送messenge給service。
Binding to a Service
應用程式組件(客戶端)能通過呼叫bindService(0方法來綁定service,android系統接著呼叫service的onBind()方法,onBind()方法會回傳IBinder為了與service互動。
綁定是異步的,bindService(0立即回傳而沒有回傳IBinder給客戶端,為了接收IBinder,客戶端必須創建ServiceConnection並傳送它給bindService()。ServiceConnection包括系統呼叫來傳遞IBinder的回呼方法。
所以,從你的客戶端綁定service,你必須:
- 實現ServiceConnection。必須複寫兩個回呼方法:
- onServiceConnected():
系統呼叫此方法來傳遞回傳自service的onBind()方法的IBinder。 - onServiceDisconnected():
當與service連結在非預期情況下消失時,系統呼叫此方法。並不是在客戶端不再綁定service時呼叫。 - 呼叫bindService(),傳遞ServiceConnection。
- 當系統呼叫onSeviceConnected()回呼方法,你能開始呼叫service的方法。
- 從service解除連結,呼叫unbindService()。
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,客戶端將ServiceConnection通過bindService()方法綁定service。
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
第一個參數是一個Intent物件,裡面明確地定義了service的名稱。
第二個參數是ServiceConnection物件。
第三個參數是綁定的選擇的旗標,
沒有留言:
張貼留言