一個Bound Service在客戶端和服務器接口中是一個服務器。Bound Service允許組件綁定到這個Service,發送請求,接受響應,甚至表現出進程間通信。Bound Service僅僅當它服務於另一個應用程序組件時纔會存活,並且不能獨立的運行在後臺。
Bound Service是一個Service類的實現,允許其他應用程序綁定他並且和他進行交互。爲了綁定一個Service,你必須實現onBind回調方法,這個方法返回一個IBind對象,這個IBind對象定義了客戶端和服務器交互的程序接口。
一個客戶端能夠調用bindService方法綁定到Service,當它連接的時候,它必須提供一個ServiceConnection的實現,它控制着與這個Service的連接。這個bindService方法不會返回任何值,但是當系統創建客戶端與服務器的連接時,它會調用ServiceConnecte類的onServiceConnected()方法,客戶端能夠用IBinder和這個Service通訊。
在同一時間,可以有多個客戶端連接到這個Service,然而僅僅當第一個客戶端和這個Service綁定的時候系統纔會調用Service的onBind方法獲取IBind對象。系統將會發送同樣的IBind到其他要綁定這個Service的客戶端,而不是再次調用oBind方法。
當最後一個客戶端和這個Service解除綁定的時候,系統就會銷燬這個Service。
當我們實現我們的Bound Service的時候,最重要的部分是定義我們Service的onBind回調方法返回的接口。我們可以有一下三種方式來定義這個接口。
第一、繼承Binder這個類
如果我們的Service是對於我們的應用程序是私有的並且和客戶端運行在同一個進程中,我們應該繼承Binder這個類來創建我們的接口,並且從onBind方法中返回一個Binder的實例。當我們要爲我們的應用程序創建一個後臺的工作時,這種做法是一種比較好的做法。
第二、使用一個Messager
如果我們希望我們的接口能夠跨進行進行工作,我們可以用Messager來爲我們的Service創建一個接口。用這種方法,我們定義一個Handler去響應不同類型的Messager對象。這個Hanlder是一個Messager對象和一個客戶端分享IBinder的基礎,運行客戶端用Messager對象發送命令給這個Service。另外,客戶端也可以定義自己的Messgaer對象,因此這個Service也能夠發送Messager給客戶端。
第三、使用AIDL
AIDL(Android Interface Define Language)所做的工作是把對象分解成操作系統能夠識別的原生的語言,並且安排他們跨進程進行進程間的通訊。以上所說的Messager技術,就是基於AIDL。正如前面所提到過的,Messager對象創建了在線程中爲所有客戶端請求創建一個請求隊列,因此這個Service一次僅能接收一個請求。然而,如果我們想要我們的Service能夠一次接受多個請求,我們可以直接使用AIDL,在這種情況下,你的Service必須能夠勝任多線程並且實現安全的。
我們爲了使用AIDL,我們需要定義一個.aidl文件,這個文件定義了程序的接口。Android SDK工具用這個文件生成了一個抽象類,這個抽象類定義了定義了接口和操作IPC的實現,在我們的Service中我們可以繼承這個抽象類。
注意:大多數情況下,我們一般來說不會使用AIDL來創建一個Bound Service,因爲它可能需要一個多線程的兼容並且導致一個更加複雜的實現。正因爲如此,AIDL不太適合大多數的應用程序,所以我們不會在這裏進行講解關於AIDL的用法。
繼承Binder類的講解
如果我們的Service僅僅需要在我們自己的應用程序中使用並且不需要跨進程工作,那麼我們可以通過繼承Binder來實現我們自己的Binder,這個Binder可以讓客戶端直接訪問我們的Service。
注意,這種方法僅僅適用於客戶端和服務端在同一個應用程序和進程中,例如,一個音樂播放應用程序需要把一個Activty和一個在後臺播放音樂的Service進行綁定的時候,這種方式將會是一個很好做法。
1、首先,在我們的Service中我們可以創建一個IBinder的實例通過一下幾種方式
1>包含這個客戶端能夠訪問的公共方法;
2>返回當前Service的實例,這個實例中的公共方法客戶端可以訪問;
3>或者,返回被這個Service主導的另一個類的實例,這個實例包含了可以被客戶端訪問的公共方法;
2、從onBinder這個回調方法中返回Binder的實例;
3、在客戶端中接受到來自onServiceConnected()回調方法;
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);
}
}
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;
}
};
}
使用Messager對象
如果我們想要和遠程的Service進行通訊,你能使用Messager對象來來爲你的Service提供接口。這種技術允許你不必通過AIDL來表現進程間通訊。
下面是一些如何使用Messager對象的總結:
1、Service實現一個Handler,這個Handler接收每一個來自客戶端請求的回調;
2、這個Handler被用來創造一個Messager對象;
3、而這個Meeager對象創建一個IBinder對象,這個Binder對象從這個Service的onBind()方法中返回到客戶端;
4、客戶端用這個IBinder實例化這個Messager,客戶端用這個IBinder給這個Service發送一個Messager對象;
5、這個Service在它的Handler對象的handleMessage()方法中接收沒一個請求。
<span style="font-size:12px;">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();
}
}</span>
<span style="font-size:12px;">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;
}
}
}</span>