Bound Services
綁定服務允許組件綁定到服務,發送請求,接收結果,甚至進程間通信。綁定服務僅僅存活於服務於其它組件的時候,它不會長期在後臺運行。
The basics
爲了提供綁定服務,你必須實現onBind方法,這個方法返回一個IBinder,定義了交互的接口。你必須實現ServiceConnection類,監控服服務的連接。bindService方法並不會立刻返回一個值,但是當系統建立了client和service的連接時,它會調用ServiceConnection的onServiceConnected方法,並且傳遞IBinder。多個clients可以綁定到一個服務,但是隻有第一個client調用onBind方法時,纔會調用onBind方法,以後就不會調用了。當最後一個client調用unBind方法時,系統會殺死這個服務(除非也調用startService)。
Creating a Bound Service
你必須提供一個IBinder,三種方法:
繼承Binder類:
如果你的service對於你的應用是私有的,並且在同一個進程中運行,你可以實現該類,並通過onBind方法返回該類的一個實例。這時,client就可以訪問IBinder和Service中的public方法。如果你的服務被其它應用使用或者運行在單獨的進程中,你不能使用這種方法。
Using a Messenger:
如果你需要一個接口工作於不同的進程,你可以爲service創建一個Messenger接口。這種方法,service定義了一個Handler處理不同類型的請求。Messenger能夠和client分享一個IBinder,允許client使用Message發送請求。通常,client也可以定義一個Messenger,這樣service可以將Message送回。
這是最簡單的進程間通信,因爲Messenger將所有的請求都入列到一個線程中,這意味着一次只能執行一個請求,所以你不必擔心線程安全問題。
Using AIDL:
AIDL做了所有的工作使得可以將對象分解成操作系統可以理解的原語,然後在不同進程間執行IPC。Messenger方法底層就是使用了AIDL 的機制實現的。如果你想service能夠同時處理多個請求,你可以直接使用AIDL。這種情況下,你必須實現多線程安全問題。
要使用AIDL,你必須創建一個.aidl文件來定義編程接口。你可以使用sdk工具來根據這個文件生成一個抽象類(繼承該接口)處理IPC。
接下來你可以在service中繼承該抽象類。
注意:大多數應用程序不該使用AIDL來創建一個bind service.因爲這會造成多線程安全問題,如果你想實現,你可以參照android的AIDL文檔。
繼承Binder類:
注意:這僅僅只有client和service在同一個應用並且在同一個進程中纔會有效。
1在service中創建一個IBinder對象:
1).包含一些public方法,這樣client可以調用
2).返回當前service對象(包含一些public方法,client可以調用),
3).或者返回一個service的內部類包含一些public方法,這樣client可以調用
2.在onBind方法中返回IBinder對象實例。
3.在client中,從onServiceConnected方法接收這個Binder。然後調用提供的方法調用服務。
注意:之所以client和service必須在同一個應用中是因爲這樣纔可以使client轉換返回的對象和調用它的apis。因爲這種方法並不會在進程間通信,所以必須在同一進程運行。
例如:
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方法給client,並返回當前service對象。這樣client就可以調用service的public方法。
下面的例子是activity調用service的public方法:
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;
}
};
}
使用Messenger:
如果你希望你的service可以使用遠程進程調用,你可以使用這種方法。它可以是你避免使用AIDL實現進程間通信。
如何使用:
1.service實現一個Handler接口,接收client的請求調用
2.這個Handler用來創建一個Messenger對象,並且包含了此handler的一個引用。
3.這個Messenger對象創建一個IBinder對象,並通過onBind方法返回給client。
4.client用這個IBinder對象實例化這個包含了service的handler的Messenger對象,client使用這個handler發送Message給service。
5.service在它的handleMessage方法中處理message。
這種方法,client不會調用service的方法,而是通過發送給service的message交給handler處理。
下面是一個例子:
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();
}
}
client僅僅只需要根據service返回的IBinder對象在ServiceConnection的onServiceConnected方法中創建Messenger對象,並通過send方法發送消息。例子:
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應答,你需要自client中創建一個Messenger,當client接收到onServiceConnected方法時,通過send方法將messenger發送給service。
注意:僅僅activity,contentprovider,service可以綁定到一個服務,broadcastreceiver不能夠綁定到一個服務。
ServiceConnection:
onServiceConnected:系統調用這個方法傳遞由onBind方法返回的IBinder
onServiceDisconnected:系統會在和服務的連接意外丟失時調用此方法。例如:當service崩潰或者被殺死。client調用unBind方法時並不會調用此方法。
當client被銷燬時,它會解除和service的綁定,但是你應該在和service完成交互時或者你的activity執行pause時解除綁定。這時,服務就可以在不用的時候結束。
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
第三個參數應該爲BIND_AUTO_CREATE,這樣在service還沒有創建時可以創建service。
需要注意的地方:
1.你必須注意DeadObjectException,當連接破壞的時候會被調用。這是唯一的一個被遠程方法拋出的異常。
2.objects是被跨進程引用計數。
3.你應該在client的匹配的生命週期進行onBind和unBind。
1)例如你只希望在activity可見時與服務通信,那麼你可以在onStart中onBind,在onStop中unBind。
2)如果你想在activity停止時仍然可以收到結果,你可以在onCreate和onDestroy中執行。必須知道的是,這意味着你的activity在整個生命週期中都會使用service。如果service運行在兩外一個進程中,所以你增加了進程的殺死權重,系統很可能會殺死它。
注意:不要再activity的onResume和onPause方法中onBind和unBind。因爲如果有多個activities綁定到一個服務。並且其中兩個有一個過渡,那麼你在過渡過程中首先可能會銷燬服務,然後在下一個activity創建服務。
管理綁定服務的生命週期
如果你的服務接受綁定,當系統調用unBind方法時,如果你希望在下一次一個client綁定到service時被系統調用onRebind方法而不是onBind方法時,你可以在unBind方法中返回true。onRebind返回void,但是client仍然可以在onServiceConnected方法中接收到IBinder。下面是這種生命週期: