创建Bound Service
想要给服务提供绑定,你就要实现onBind()回调方法。这个方法返回一个IBinder的对象,它用于定义应用与service进行交互的编程接口。有三种方法可以定义这个接口:
Extending theBinder
class:如果你的服务是给你自己的应用程序去用,那么你应该继承Binder类,创建属于你自己的接口并接收onBind()方法的返回值。Activity接收Binder,然后直接可以通过公共方法使用这个Binder而不用实现Binder甚至是Service。这个服务当它在后台运行,且只给你运行的时候使用这个方法是最好的了,但是如果你的服务不是私有的,而是让其他的应用程序也会用到,那么你最好还是不要选择这个方法。因为它跨越了不同的进程。
Using a
Messenger:如果你需要你的接口要在不同的进程中使用,你就应该使用Messenger为服务创建接口。在这种方式中,service定义Hander用于解决不同类型的Message对象。Handler是Messenger的基础,它可以分享IBinder对象给client,允许类通过Message对象发送命令到service。除此之外,你可以定义属于你自己的Messenger,这样service就能把消息发送回来了。
这个方式是实现IPC最简单的一种方式,因为Messenger
quenes是一种单线程的东西而且你设计你服务的时候不用考虑线程安全问题。
Using AIDL:这个问题我们在AIDL那篇博客详细的说过,详细的内容请看那篇关于AIDL的叙述,这里就不赘述了。
(注:大部分的应用程序不应该使用AIDL创建bound
service,因为它可能需要你有多线程并发的解决能力,同时,它也会使得程序变得很复杂)
继承Binder类
如果你的服务只是使用自己的应用程序而且不需要通过进程来操作其他应用程序,那么你就可以实现你自己的Binder类,在serivce中直接让类调用它的公共方法。
(注:这个情况只能是在应用程序和服务都在一个线程之中,尤其是在前台的service操作后台的播放动作这样的音乐应用上编辑最为有效果)
你只需要做以下几个步骤:
1. 在你的service中创建一个Binder的实例化对象,或者:
·包含一个其他类可以调用的方法;
·返回当前服务的实例,这样有公共方法的类就能够调用它;
·返回一个其他类创建服务的实例,这样有公共方法的类就能够调用它;
2. 从onBind()回调方法中返回Binder类的实例化对象;
3. 在类中,接收从onServiceConntected()回调方法的Binder然后通过使用提供的方法调用bound
service;
(注:为什么非要让serivce和类都要在一个应用程序中,就是因为这样做的话类就能正确的收到传递过来的返回对象。Service和类也一定要在相同的进程中,因为这个方法不提供分发数据到其他进程的机制)
例如,下面这个方法就是通过Binder实现的与service连接
public class LocalServiceextends Service {
// Binder given to
clients
private final IBinder mBinder
= new LocalBinder();
// Random number
generator
private final Random
mGenerator = new Random();
public class LocalBinder
extends Binder {
LocalService getService() {
// Return
this instance ofLocalService so clients can call public
methods
return
LocalService.this;
}
}
@Override
public IBinder onBind(Intent
intent) {
return mBinder;
}
public int getRandomNumber()
{
return
mGenerator.nextInt(100);
}
}
LocalBinder给类提供getService()方法用于检索当前LocalService的实例。这允许类调用服务中公共的方法。例如,该类service中调用getRandomNumber()方法。
public class BindingActivityextends 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;
}
}
public void
onButtonClick(View v) {
if (mBound) {
// Call a
method from theLocalService.
//
However, if this call weresomething that might hang, then this
request should
// occur
in a separate thread toavoid slowing down the activity
performance.
int num =
mService.getRandomNumber();
Toast.makeText(this, "number:" + num,
Toast.LENGTH_SHORT).show();
}
}
private ServiceConnection
mConnection = new ServiceConnection() {
@Override
public void
onServiceConnected(ComponentNameclassName,
IBinder service) {
// We've
bound to LocalService, castthe IBinder and get LocalService
instance
LocalBinder binder = (LocalBinder)service;
mService =
binder.getService();
mBound =
true;
}
@Override
public void
onServiceDisconnected(ComponentNamearg0) {
mBound =
false;
}
};
}
上面是一个简单的例子介绍bound service的使用方法,下面将会再进行详细的讨论。
Usinga Messenger
如果你需要使用服务交流远程的进程,那么你就可以使用Messenger为服务提供这个接口,它使你最简单的实现进程间通信。
下面说一说如何使用Messenger:
·service实现了Handler类用于接收从其他类发来的回调请求;
·Handler用于创建Messenger对象;
·Messenger创建IBinder类用于接收onBind()方法中发送过来的返回值;
·某个类使用IBinder类来实例化Messenger,用于发送Message对象给service;
·service在Handler中接收每一个Message,在handleMessage()方法中;
在这种情况中,没有类调用方法给service。取而代之的是,类传递“messages”然后service用它的Handler接收。
public class MessengerServiceextends Service {
static final int
MSG_SAY_HELLO = 1;
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);
}
}
}
final Messenger mMessenger =
new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent
intent) {
Toast.makeText(getApplicationContext(),
"binding",Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
值得注意的是,在Handler中使用handleMessage()方法接收即将到来的Message,是使用what关键字的;
你只要在类中创建一个基于被service返回的IBinder的Messenger然后通过send()方法发送一条message。例如,下面这个例子就是绑定到service上然后给服务发送MSG_SAY_HELLO消息;
public class ActivityMessengerextendsActivity {
Messenger mService =
null;
boolean mBound;
private ServiceConnection
mConnection = new ServiceConnection() {
public void
onServiceConnected(ComponentNameclassName, IBinder service) {
// This is
called when the connectionwith the service has been
//
established, giving us the objectwe can use to
//
interact with the service. We are communicating
with the
// service
using a Messenger, so herewe get a client-side
//
representation of that from theraw IBinder object.
mService =
new Messenger(service);
mBound =
true;
}
public void
onServiceDisconnected(ComponentNameclassName) {
// This is
called when the connectionwith the service has been
//
unexpectedly disconnected -- thatis, its process crashed.
mService =
null;
mBound =
false;
}
};
public void sayHello(View v)
{
if (!mBound) return;
// Create and send a message to the service,
usinga 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;
}
}
}
值得注意的是,这个例子中没有展示service如何对类响应。如果你想让service对其进行响应,那你需要在你的类中创建一个Messenger对象,然后当该类接收到onServiceConnected()回调方法的值时,它会给service发送一条Message,这条Message包含该类的Messenger,这个Messenger的类型是send()方法中的replyTo参数。
此外,你还需要好好看看MessengerService.java和MessengerServiceActivities.java两个类
Binding to aService
类可以通过调用bindService()方法绑定到service上,然后android系统调用service的onBind()方法,返回一个IBinder类的实例化对象用于与服务交互。
这个绑定是异步的。bindService()会立刻返回而且不会把IBinder返回给类。为了接收这个IBinder,类必须创建一个ServiceConnection的实例化对象然后把它传递给bindService()。然后ServiceConnection包含了一个系统调用传送IBinder的回调方法。
(注:只有activites、services、content
providers可以绑定服务,你不能使用contentprovider绑定服务)
所以,你的类要想绑定service,你一定要:
1. 实现ServiceConnection
你实现的时候一定要重载以下两个回调方法:
OnServiceConnected():系统调用这个方法来传递被服务的onBind()方法返回的IBinder
onServiceDisconnected():当正在连接的服务发生异常时系统调用这个方法
2. 调用bindService()方法,传递Serviceconnection的实现实例
3. 当系统调用你的onServiceConnected()回调方法,你可以使用你定义的接口的方法开始调用服务了。
4. 断开与service的连接,调用unbindService()方法。
例如,下面这段代码就是通过继承了Binder类把该类与服务连接,所以你必须要传递一个IBinder的返回值给LocalService类然后实例化LocalService
LocalServicemService;
private ServiceConnection mConnection = new ServiceConnection()
{
// Called when the connection
with the service is established
public void
onServiceConnected(ComponentName className, IBinderservice) {
// Because we have bound to an explicit
// service that is running in our own process,
wecan
// cast its IBinder to a concrete class
anddirectly access it.
LocalBinder binder = (LocalBinder)
service;
mService = binder.getService();
mBound = true;
}
// Called when the connection
with the service disconnectsunexpectedly
public void
onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false;
}
};
使用Serviceconnection,类可以绑定到服务上通过bindService,例如:
Intent intent = newIntent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
·bindService的第一个参数是一个intent
·第二个参数时Serviceconnection对象
·第三个参数是一个声明,通常是BIND_AUTO_CREATE,为了创建不存在的serivce。
额外的一些注意事项
·你可能经常遇到DeadObjectException,就是当远程连接时发生中断会出现的异常
·多个进程,对象都被当成了引用
·如果你想要你的activity接受回应当它在后台停止的时候,那么你就绑定onCreate()而在onDestory()中释放绑定,这就意味着你的activity需要在整个运行期一直使用服务,所以如果service在另一个进程中,你就会增加进程的负重从而出现被系统杀掉的风险;
(注:你不应该经常绑定和取消绑定在你activty调用onResume()和onPause()方法)
管理Bound Service的生命周期:
当service没有绑定时,系统就会销毁它。这样,如果service纯粹是一个纯粹的Bound
Service,你不必管理服务的生命周期。
然而,如果你选择实现onStartCommand()回调方法,然后你必须明确的停止service,因为这个服务被认为已经开启了。在这种情况下,service会一直运行直到service停止自己或者调用stopService(),不管它是否绑定到了任何类。
除此之外,如果你的service已经开启了并且接受binging,然后当系统调用你的onUnbind()方法,你就可以返回true如果想要接受onRebind()的调用。OnRebind()返回void,但是类依然接收IBinder在它的onServiceConnected()回调方法。上面可能说的有点乱,下面看看这个图吧:
笔者个人理解:通过对bound
Service的学习,这样就会发现,我们完全可以通过里面的Messenger去实现,接下来会做一个通过Bound
Service
实现的Demo。本人个人翻译能力有限,如有不足,欢迎斧正。