这一章介绍bound service(我也不知道怎么翻译比较好。。)。建议先看前一章对服务有个大概的了解了再看bound service。
Android四大组件之Service第一章:service及其生命周期
bound service是一种采用了client-server架构(CS架构)的接口,它使得组件同一个服务进行绑定后的发送请求,接收结果,IPC通信成为可能。本文将介绍如何创建一个bound service并与之绑定。
基础知识
在service这边,为了实现绑定,必须重写onBind()方法,该方法返回一个IBinder对象,这个对象决定了client与server之间的通信方式。
在client这边,必须要调用bindService()与服务绑定,同时要提供一个ServiceConnection对象,这个对象会监控client与server之间的绑定情况。当二者的绑定关系建立的时候,就会调用ServiceConnection的onServiceConnected()方法并传入一个IBinder对象,这个对象就是service那边的onBind()方法执行完之后返回的IBinder对象。可以说ServiceConnection是二者之前建立关系的桥梁。
一个service可以同时绑定多个client,但是其onBind()方法只会在首次绑定的时候调用,之后绑定更多client的时候不会再调用这个方法,但是之后绑定的client依然会收到同样的IBinder对象。(作者注:可见onBind()方法返回的IBinder对象并不是直接返回给client,而是由系统接管,再派发给client。)
当最后一个client解绑的时候,系统将会销毁这个服务,除非这个服务还是一个started service。
对于一个bound service来讲,最重要的是如何与client通信,即返回一个什么样的IBinder。下面介绍三种不同的IBinder。
创建一个bound service
创建一个bound service的核心在于要能够提供一个client能够和该service进行交互的IBinder对象(准确来说是接口,因为IBinder是接口,不能生成对象)。有三种方式来提供这种接口:
1. 继承Binder类
如果一个服务只为自身所在的应用程序服务,并且在同一个进程中跑,那么继承Binder类,并在onBind()方法中返回该类对象的方式是最合适的。client将会使用这个Binder对象来调用Binder或Service中的public方法。
如果一个服务可能被其他程序所用或者需要运行在不同的进程中,那么就无法使用该方式,否则推荐使用该方式来创建一个bound service。
2. 使用Messenger
如果你需要跨进程通信,那可以使用Messenger通信的方式来提供这个操作接口。在这种方式下,Service内部有一个Handler对象用来处理各种不同的Message对象,根据这个Handler对象可以创建一个Messenger。这里很难说清楚,具体看下面的代码示例吧。
如果要进行IPC的话,使用Messenger是最简单的方式了,因为Messenger会将所有的请求列队,依次处理。因此不需要担心线程安全问题。
3. 使用AIDL
上面的Messenger方式实际上是基于AIDL。正如上面提到的那样,Messenger会将所有的请求列队,依次处理。如果你一定要你的service能够同时处理多个request,那就直接用AIDL吧。注意线程安全问题哦。
大多数应用程序不应该直接使用AIDL来创建bound service,因为这会让逻辑变得很复杂。本章节不讨论直接使用AIDL的方式。
继承Binder类
如上所述,如果一个服务是一个本地服务,并且不需要跨进程,那么可以使用这种方式向client提供一个Binder对象使得其能够直接操作service中的public方法。
使用步骤
- 在service的内部生成一个Binder对象(成员变量),该Binder对象需要符合以下几个特征之一:
- 该Binder对象本身包含一些public方法
- 该Binder对象能够返回其所在的Service对象,并且该Service对象中要包含public方法
- 该Binder对象能够返回Service中的另外一些包含public方法的对象
- 在onBind()方法中返回该Binder对象
- 在client中,从onServiceConnected()方法中接收到这个Binder对象,进而通过其调用一些方法。
service和client必须在同一个应用程序中才行,这样client才能对收到的Binder对象进行正确的cast并调用一些api。同时service和client也必须在同一个进程中,因为该技术并不支持进程间的通信。
总结一下继承Binder类的方式就是:向client提供引用,然后client使用该引用直接调用对象的public方法。
以下是代码示例:
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);
}
}
上面的代码中有个关键方法:getService()。该方法返回了一个Service对象的引用,然后client直接使用这个引用来操作对象的public方法。下面来看看Activity(client)中的代码:
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;
}
};
}
可以看到在client的onStart()方法中进行了对服务的绑定操作,传入了一个ServiceConnection对象。在绑定关系建立的时候,会在ServiceConnection对象的onServiceConnected()方法中收到Binder对象,然后通过该Binder对象的getService()方法获得Service的引用,然后直接调用service的public方法。
在该示例中,在onStop()方法中对Service进行了unBind,client应该懂得在合适的时候对绑定的服务进行解绑,具体参加下面的附加部分。
使用Messenger
如果你的服务需要跨进程通信,那么可以使用Messenger来提供通信的接口。
使用Messenger的步骤如下:
- 在Service中创建一个Handler对象,用来处理所有的请求。该Handler会持有一个IMessenger引用。
- 使用这个Handler创建一个Messenger对象。该Messenger也会持有同一个IMessenger的引用。
- 使用Messenger创建一个IBinder对象,并在onBind()方法中返回。
- client收到Binder对象后使用它创建一个Messenger对象,由于这个Messenger是使用Binder对象创建的,而Binder对象是Service中的Messenger创建的,而Service中的Messenger是使用Service中的Handler创建的,所以client中的Messenger和Service中的Handler就这样建立了联系。
- 在client中使用Messenger对象发送消息。Service中的Handler就会收到消息。准确来讲是Handler的handleMessage()方法会收到消息。
通过这种方式,client不会直接调用service的任何方法,而是通过message来传递信息。下面是例子:
Server端:
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;
}
}
}
这里的关键点在onServiceConnected()方法中使用传进来的IBinder对象new了一个Messenger,由于这个IBinder是和Service中的Handler有关系的,因此将client中的Messenger与service中的Handler建立了联系。然后就可以在client中通过Messenger发送消息了。
感觉Service端的Messenger只是起了个构建IBinder的作用,它自己本身并没有传到client中去。Client端是利用这个IBinder对象又new了一个Messenger。
上面的代码例子只能建立一个单向的通信通道,即service无法向client返回结果。如果你需要service对client的请求返回一个结果的话,你需要在client中创建一个Messenger(注意:这个Messenger不是上面那个Messenger,下面有代码示例),然后发送message的时候将这个Messenger对象封装到message中去(Message有一个字段为Messeneger类型,名为replyTo,即发送的消息中包含处理完这条消息要向谁返回结果的信息)。Service收到消息后,需要返回结果的话,取出这个replyTo,使用它再向client发一条消息。方式和client向serveice发送消息是一样的。说再多还不如 show me the code!直接在上面的代码基础上进行更改!
client端:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
int GIVE_ME_A_RESPONSE = 100;
int RESPONSE_FROM_SERVICE = 110;
Messenger activityMessenger = new Messenger(new ActivityHandler());
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/** this handler is created by the activity to handle response from service. */
class ActivityHandler extends Handler{
@Override
public void handlerMessage(Message msg){
switch(msg.what){
case RESPONSE_FROM_SERVICE:
Toast.makeText(ActivityMessenger.this, "response from service received",
Toast.LENGTH_SHORT).show();
break:
default:
super.handlerMessage(msg);
}
}
}
/**
* 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);//this Messenger is based on the Handler of 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();
}
}
public void sentMsgToServiceWithResponse(){
if (!mBound) return;
try {
Message msg = Message.obtain(null, GIVE_ME_A_RESPONSE)
msg.replyTo = activityMessenger;
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端:
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();
case ActivityMessenger.GIVE_ME_A_RESPONSE:
Message msg = Message.obtain(null, ActivityMessenger.RESPONSE_FROM_SERVICE);
msg.replyTo.send(msg);//retrieve the replyTo messenger to send back a response message
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();
}
}
仔细观察上面的代码会发现,service返回结果给client的通信方式和client发送消息给service的通信方式完全相同!
与服务绑定
应用程序组件(client)通过调用bindService()方法与服务绑定。系统随即调用服务的onBind()方法,返回一个IBinder给client用来通信。
绑定的过程是异步的,bindService()方法会立即返回,而不会等IBinder对象传给client。要接收这个IBinder对象,必须在bindService()的时候传入一个ServiceConnection对象。系统会回调它的onServiceConnected()方法并传入IBinder对象。
注意:只有Activity,Service,ContentProvider能够和Service绑定,广播接收器不能。
如上面的代码中所示,绑定一个服务需要如下几个过程:
创建一个ServiceConnection对象,该对象必须实现下面两个方法:
onServiceConnected()
系统通过回调该方法来传入IBinder对象
onServiceDisconnected()
当client与server之间的链接意外断开的时候,系统会回调该方法,比如当service崩溃或者被系统杀掉的时候。正常的解绑操作不会回调该方法。
调用bindService(),将上一步的ServiceConnection对象传进去。
当绑定关系建立后,在onServiceConnected()中使用IBinder接口来与服务通信。
要与服务解绑,调用unbindService()
如果不解绑或者还没来得及解绑,这个client就被app销毁了,那么将自动解绑。最好是在服务完成工作之后立即解绑,这样可以关闭空闲服务。
关于bindService()的三个参数:
- Intent:上一篇关于服务的文章中也说过了,启动服务不要使用隐式Intent。
- ServiceConnection:前面讲了
- int:这个值决定了绑定后的服务要进行什么操作,有好多种值,具体查阅API文档。