Bound Service 是客户端-服务器模式的服务,它允许允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。为了提供绑定,开发人员必须实现onBind()毁掉方法,该方法返回IBinder对象,它定义了客户端用来与服务交互的程序接口。
客户端通过bindService()方法绑定到服务。此时,客户端必须提供ServiceConnection接口的实现类,它监视客户端和服务之间的连接。多个客户端能够同时连接服务器。然而仅当第一个客户端绑定时,系统调用服务的onBind()方法来获取IBinder对象。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder ,而不再调用 onBind() 。
当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过 startService() 启动的)。
一:创建Bound服务
当你实现自己的bound服务时,最重要的工作就是定义 onBind() 回调方法所返回的接口。 有一下三种方式:
(1)继承Binder类
如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过j继承 Binder 类来创建你的接口,并从 onBind() 返回一个它的实例。客户端接收该 Binder 对象并用它来直接访问 Binder 实现类或者 Service 中可用的公共(public)方法。
如果服务只应用于私有应用程序,那这就是首选的技术方案。如果服务要被其它应用程序使用或者访问独立进程时,则不能使用该技术。
仅当客户端与服务位于同一个应用程序和进程时,才可以。例如音乐播放我绑定activity到自己的服务来在后台播放音乐。
实现步骤如下:
- 在你的服务中,创建一个 Binder 的实例,实现以下三者之一:
(1)包含客户端能调用的公共方法
(2)返回当前service实例,其中包含客户端能够调用的公共方法
(3)返回服务管理的其他类的实例,其中包含客户端能够调用的公共方法
- 从回调方法 onBind() 中返回 Binder 类的实例。
- 在客户端中,在回调方法 onServiceConnected() 中接收 Binder 类实例并用所提供的方法对绑定的服务进行调用。
以下是一个服务的示例,它通过实现一个 Binder 来为客户端访问它内部的方法提供支持:
public class LocalService extends Service {
// 给客户端的Binder
private final IBinder mBinder = new LocalBinder();
// 产生随机数
private final Random mGenerator = new Random();
public class LocalBinder extends Binder {
LocalService getService() {
// 返回LocalService 的实例,其中包含客户端可以调用的方法
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**客户端可以调用的方法 */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
LocalBinder为客户端提供了getService()方法,用于返回当前LocalService的实例。 这就让客户端可以调用服务中的公共方法。比如,客户端可以调用服务中的getRandomNumber()。
以下是一个绑定到LocalService的activity,当点击按钮时,它会调用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();
// 绑定到 LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 解除绑定的服务
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** 点击Button时,调用此方法 */
public void onButtonClick(View v) {
if (mBound) {
// 调用LocalService中的方法。
// 不过,如果该调用会导致某些操作的挂起,那么调用应该放入单独的线程中进行,
// 以免降低activity的性能。
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/** 定义服务绑定的回调方法,将其传给bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// 我们已经绑定了服务, 将IBinder转型获得LocalService实例
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
上述例子展示了客户端如何利用 ServiceConnection 和 onServiceConnected() 回调方法绑定到服务。
(2)使用Messenger类
如果你的服务需要与远程进程进行通信,那你可以使用一个 Messenger 来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC),这是执行进程间通信(IPC)最为简便的方式。
以下是使用方法:
- 服务实现一个 Handler ,用于客户端每次调用时接收回调。
- 此 Handler 用于创建一个 Messenger 对象(它是一个对 Handler 的引用)。
- 此 Messenger 对象创建一个 IBinder ,服务在 onBind() 中把它返回给客户端。
- 客户端用 IBinder 将 Messenger (引用服务的 Handler )实例化,客户端用它向服务发送消息对象 Message 。
- 服务接收 Handler 中的每个消息 Message ——确切的说,是在 handleMessage() 方法中接收。
通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”( Message 对象),服务则接收位于 Handler 中的这个消息。
public class MessengerService extends Service {
static final int MSG_SAY_HELLO = 1;
/**
* 服务处理客户端发来的消息
* 服务实现了一个Handler
*/
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);
}
}
}
/**
* 给客户端使用的用来发送消息给 IncomingHandler.即为,第二步。
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* 返回一个接口用来给服务发送消息,即为,第三步
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
客户端要做的全部工作就是根据服务返回的 IBinder 创建一个 Messenger ,并用 send() 方法发送一个消息。例如,以下是一个activity示例,它绑定到上述服务,并向服务发送 MSG_SAY_HELLO消息:
public class ActivityMessenger extends Activity {
/** 和service沟通的Messenger. */
Messenger mService = null;
/** 是否已经绑定 */
boolean mBound;
/**
* 与服务进行交互
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// 与服务建立联接后将会调用本方法,
// 给出用于和服务交互的对象。
// 我们将用一个Messenger来与服务进行通信,
// 因此这里我们获取到一个原始IBinder对象的客户端实例。
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// 当与服务的联接被意外中断时——也就是说服务的进程崩溃了,
// 将会调用本方法。
mService = null;
mBound = false;
}
};
public void sayHello(View v) {
if (!mBound) return;
// 创建并向服务发送一个消息,用到了已约定的'what'值
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();
//绑定到服务
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 解除绑定
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
}
二:绑定到服务
应用程序组件(客户端)可以通过调用 bindService() 来绑定服务。然后Android系统会调用服务的 onBind() 方法,返回一个用于和服务进行交互的 IBinder 。
绑定是异步进行的。 bindService() 将立即返回,并不会向客户端返回 IBinder 。为了接收 IBinder ,客户端必须创建一个 ServiceConnection 的实例,并把它传给 bindService() 。 ServiceConnection 包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个 IBinder 。
注意: 只有activity、service和content provider才可以绑定到服务上——broadcast receiver不能绑定服务。
因此,要把客户端绑定到服务上,你必须:
- 实现 ServiceConnection 。你的实现代码必须重写两个回调方法:onServiceConnected()系统调用该方法来传递服务的 onBind() 方法所返回的 IBinder 。onServiceDisconnected()当与服务的联接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会调用该方法。客户端解除绑定时,不会调用该方法。
- 调用 bindService() ,传入已实现的 ServiceConnection 。
- 当系统调用你的 onServiceConnected() 回调方法时,你可以利用接口中定义的方法开始对服务的调用。
- 要断开与服务的联接,请调用 unbindService() 。
当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// 与服务的联接建立之后将会调用
public void onServiceConnected(ComponentName className, IBinder service) {
// 因为我们已经与明显是运行于同一进程中的服务建立了联接,
// 我们就可以把它的IBinder转换为一个实体类并直接访问它。
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// 与服务的联接意外中断时将会调用
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};
利用这个 ServiceConnection ,客户端就能够把它传入 bindService() 完成与服务的绑定。
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);