一个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>