绑定状态下的service
Bound Service是一种c/s接口中的server端。其允许其他组件绑定到service,发送请求,接收响应,甚至进行进程间的交互(IPC)Bound Service通常只在有其他组件需要服务时运行,并不单独运行在后台。
本文档主要内容是如何创建一个bound service,包括在其他应用中绑定该service。如果你对service的基础概念不是太熟悉,建议你先看看我的另外一篇博客Android开发文档翻译之-Services
基础概念
Bound Service是service的一种实现方式。通过该方式其他应用程序可以绑定到该service并与其交互。你需要覆写onBind()函数来提供该功能。这个方法返回一个IBinder对象用来提供与service交互的接口。
client端程序通过调用bindService()来绑定service。调用该方法需要提供一个ServiceConnection对象,来监视与service的连接状态。bindService()会立即返回,并且在系统创建连接后回调onServiceConnected()函数,并且返回IBinder对象来供client端交互。
多个client端可以在同一时间绑定同一个service。然而用户只会接到一个onBind()回调。除了第一个绑定到service的对象会通过onBind()得到一个IBinder对象,其他时候系统都会直接将这个IBinder对象返回,并且不再调用onBind()函数。当最后一个client解绑时,系统会销毁这个service。
当你实现你的bound service时,最重要的一步是定义IBinder接口。以下是几种定义IBinder接口的方式:
1.继承Binder类
通常而言,如果你的service仅提供给你自己的应用程序使用,你应该提供你的接口通过继承Binder类,并且在onBInd()中返回。client端接收这个Binder对象,并且可以直接访问它的共有方法以及service中的共有方法。
2.使用Messenger
如果你需要一个在不同进程间工作的接口,你可以通过Messenger来创建service。这种方式下,service定义一个Handler来响应不同类型的Message。Handler类是用来提供一个IBinder对象的基础。通过Messenger,client端可以发送消息到server端。同样的,client端可以定义一个Messenger来接收service的消息回调。
注:这是IPC机制的一种简单方式。由于Messenger将所有的请求放入一个单线程的消息队列中,因此你设计的service是线程安全的。
3.使用AIDL
AIDL(Android Interface Define Language)是一种描述进程间通信的一种语言。刚刚介绍的Messenger就是一种基于AIDL的技术。如果你希望你的service能够处理多个并发的请求,那么你可以直接使用AIDL。这种情况下,你的service必须有多线程的执行能力并且需要是线程安全的。
PS:大部分程序是不需要AIDL的。因为AIDL需要拥有多线程处理能力,并且会使得情况更加复杂。因此,AIDL对于绝大部分应用是不适用的。本文也不打算介绍该技术,如果你确定你要使用该技术,请查看我的另外一篇博文:Android开发文档翻译之——AIDL
继承Binder类
你可以按以下步骤来实现:
1.在service中,创建一个Binder的实例,该Binder类需要满足:1)包含公有方法供client端调用;2)返回当前service的实例,该实例中包含公有方法给client端调用;3)如果不满足2),则返回一个service的内部类的实例,该内部类中提供了公有方法供client端调用。
2.在onBind()方法中返回Binder的实例。
3.在client端,通过onServiceConnected()函数接收Binder对象并且调用相关接口方法。
ps:service和client需要在同一应用的一个理由是你需要强制转换返回的IBinder对象。service和client必须要在相同的进程,因为该技术没有提供任何跨进程间通信的方式。
以下是一个继承IBinder类的示例:
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的实例。因此client端可以调用service的公有方法。例如,getRandomNumber()。
以下是一个activity绑定service并调用其公有方法的相关代码:
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需要和远端进程进行通信,那么你需要使用Messenger为你的service提供一个接口。这个技术允许你在不通过AIDL技术的前提下完成IPC。
以下是使用Messenger的步骤:
1.service实现Handler类来接收client端的回调。
2.通过该Handler类产生Messenger的实例。
3.Messenger产生一个IBinder类的实例并在onBinder()中返回。
4.client端使用IBinder对象来实例一个Messenger对象(该对象持有Handler的引用),通过该对象来向service端发送消息。
5.service在handlerMessage()中执行相关操作。
以下是一个使用Messenger的示例:
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端的代码如下:
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;
}
}
}
上面两段代码较为简单,就不做过多的解释了。
Messenger和AIDL的比较
当你需要进程间通信时,使用Messenger是比AIDL更为简单的一种方式,因为Messenger依次的将请求放进请求队列中,而AIDL同时的将所有请求发送至service,因此你需要处理多线程的问题。
大部分应用程序不需要处理多线程情况,因此Messenger是更好的选择。如果你确实要在多线程情况下处理问题,那么使用AIDL来实现吧。
管理Bound Service的生命周期
Bound Service的生命周期如上图所示,在此不再赘述。