绑定服务
在该文献
基础
创建绑定服务
扩展类粘结剂
使用信使
绑定到一个服务
管理绑定服务生命周期
服务
ServiceConnection
IBinder
样本
RemoteService
本地服务
也可以看看
服务
绑定的服务是在客户端 - 服务器接口的服务器。绑定的服务允许组件(如活动)绑定到该服务,发送请求,接收响应,甚至进行进程间通信(IPC)。同时它还有另一个应用程序组件并不会在后台运行下去一个绑定服务通常只生活。
本文将向您展示如何创建绑定服务,包括如何绑定到其他应用程序组件的服务。但是,你也应该参考服务文档,对一般的服务,比如如何从服务交付通知,设置服务在前台运行,以及更多其他信息。
基础
绑定的服务是服务类,允许其他应用程序绑定到它与它互动的实现。为了提供一个服务绑定,则必须实现onBind()回调方法。此方法返回一个IBinder对象,定义编程接口,客户端可以使用与服务进行交互。
绑定到一个启动的服务
服务文档中所讨论的,您可以创建一个服务,既是开始和束缚。也就是说,该服务可通过调用startService(),它允许服务无限期运行开始,并且还允许客户机通过调用bindService绑定到服务()。
如果你允许你的服务要启动和约束,那么当该服务已启动,系统不会在所有客户端解除销毁服务。相反,你必须明确停止服务,通过调用stopSelf()或stopService()。
虽然你通常应该要么实现onBind()或onStartCommand(),有时需要同时实现。例如,音乐播放器可能会发现它很有用,允许其服务无限期地运行,同时提供绑定。通过这种方式,一个活动可以启动服务来播放音乐和音乐继续即使用户离开该应用程序播放。然后,当用户返回到应用程序,该活动可以绑定到服务恢复重放的控制。
请务必阅读有关管理绑定的服务生命周期中,大约添加时绑定到一个启动的服务的服务生命周期的更多信息一节。
客户端可以通过调用bindService绑定到服务()。当它,它必须提供ServiceConnection的实施方案,其监视与该服务的连接。该bindService()方法立即返回没有价值,但是当Android系统会在客户端和服务之间的连接,它呼吁ServiceConnection onServiceConnected(),为客户提供的IBinder客户端可以使用的服务进行通信。
多个客户端可以连接到服务一次。但是,系统调用服务的onBind()方法时,第一个客户端绑定到只检索的IBinder。然后,系统会提供相同的IBinder来结合任何额外的客户端,而不会再次调用onBind()。
当最后一个客户端从服务解除绑定,系统破坏服务(除非该服务也被startService启动())。
当你实现你的绑定服务,最重要的部分是定义你的onBind()回调方法返回的接口。有几种不同的方法可以定义服务的的IBinder接口和以下部分讨论每种技术。
创建绑定服务
当创建一个提供绑定服务,你必须提供提供编程接口,用户可以用它来与服务交互一个IBinder。有三种方法可以定义界面:
扩展类粘结剂
如果您的服务是专用于自己的应用程序,并在同一个进程中的客户端(这是常见的)运行时,你应该通过扩展类粘合剂和onBind返回它的一个实例()创建界面。客户端接收活页夹,并可以使用它来直接访问无论是在Binder实现甚至服务可用的公共方法。
这是当你的服务仅仅是自己的应用程序后台工作的首选技术。唯一的原因,你会不会创建界面这种方式是因为你的服务被其他应用程序或通过单独的进程。
使用信使
如果您需要的接口在不同的流程工作,你可以创建一个信使服务的界面。以这种方式,该服务定义了响应不同类型的消息的对象的处理程序。此处理程序是一个信使,然后可以与客户共享一个IBinder,允许客户使用Message对象将命令发送到服务的基础。此外,客户端可以定义自己的使者所以这项服务可以将消息发送回。
这是执行进程间通信(IPC)最简单的方法,因为Messenger的队列中的所有请求到一个单一的线程,这样你就不必来设计你的服务是线程安全的。
使用AIDL
AIDL(Android界面定义语言)执行所有工作分解物进入原语操作系统能够理解和马歇尔他们整个进程来执行IPC。以前的技术,采用了信使,实际上是基于AIDL作为其基础结构。如上所述,信使创建在单一线程的所有客户机请求的队列,因此,服务接收一次请求之一。但是,如果你希望你的服务能够同时处理多个请求,那么你可以直接使用AIDL。在这种情况下,服务必须能够多线程和建立线程安全。
直接使用AIDL,必须创建一个定义编程接口的.aidl文件。在Android SDK工具使用这个文件来生成实现该接口并处理IPC,然后你就可以你的服务中扩展的抽象类。
注意:大多数应用程序不应使用AIDL创建绑定的服务,因为它可能需要多线程功能,可以导致更复杂的实现。因此,AIDL是不适合大多数的应用程序和本文档不讨论如何使用它为你服务。如果你确信你需要直接使用AIDL,看到AIDL文件。
扩展类粘结剂
如果您的服务只能由本地应用程序,也不需要工作跨进程,那么你就可以实现自己的活页夹类,提供了对服务的公共方法您的客户端直接访问。
注意:这只有在客户端和服务都在相同的应用程序和过程,这是最常见的。例如,这将为需要的活性绑定到其自己的服务正在播放音乐的背景音乐应用工作得很好。
以下是如何设置它:
在服务中,创建活页夹的实例,要么:
包含公共方法,客户端可以调用
返回当前服务的实例,它具有公共方法的客户端可以调用
或者,返回由服务与公共方法的客户端可以调用托管于其他类的实例
从onBind()回调方法返回粘结剂此实例。
在客户端,接收来自onServiceConnected()回调方法活页夹,并使用所提供的方法来绑定服务电话。
注意:服务和客户端必须在同一应用程序的原因是这样的客户机可投返回的对象和正确地调用它的API。服务和客户端也必须是在相同的过程中,由于该技术不跨进程执行任何编组。
例如,下面是为客户提供访问通过一个活页夹的服务实现方法服务:
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()方法,为客户获取本地服务的当前实例。这使得客户端来调用服务的公共方法。例如,客户可以从服务调用getRandomNumber的()。
下面是结合本地服务,并单击按钮时调用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();
// 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;
}
};
}
上面的示例显示了客户端使用ServiceConnection和onServiceConnected()回调的实现如何绑定到该服务。下一节提供有关绑定到服务这一过程的详细信息。
注意:上述实施例没有明确从服务解除绑定,但所有的客户端应在适当的时间解除(当活动暂停如)。
欲了解更多示例代码,请参见LocalService.java类和ApiDemos的LocalServiceActivities.java类。
使用信使
相比AIDL
当您需要执行IPC,使用信使为你的界面比AIDL实现更简单,因为信使队列服务的所有电话,而一个纯粹的AIDL接口发送到服务,则必须处理多线程同时请求。
对于大多数应用,该服务并不需要执行多线程,因此使用信使允许服务一次处理一个呼叫。如果您的服务是多线程是很重要的,那么你应该使用AIDL来定义你的界面。
如果您需要的服务与远程进程进行通信,那么你可以使用一个使者提供的接口为您服务。这种技术可以让您无需使用AIDL进行进程间通信(IPC)。
这里有一个如何使用Messenger的总结:
的服务实现了接收来自客户端的每个呼叫的回调处理程序。
该处理器是用于创建对象的使者(这是对处理程序的引用)。
信使创建一个IBinder该服务从onBind返回到客户端()。
客户端使用的IBinder实例的使者(引用该服务的处理程序),客户端用来发送消息对象的服务。
服务接收其处理程序,明确每一个消息,中的handleMessage()方法。
以这种方式,不存在“的方法”为客户端上的服务调用。相反,客户端提供了“消息”(Message对象),该服务在其处理程序接收。
下面是一个使用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();
}
}
请注意,在处理程序中的handleMessage()方法就是服务接收传入的消息,并决定做什么,基于什么部件上。
所有的客户需要做的是建立基于服务返回的一个的IBinder Messenger和使用发送发送消息()。例如,这里有一个简单的活动结合服务,并提供了MSG_SAY_HELLO消息服务:
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()回调,这将消息发送到包括该客户端的使者在send()方法的参数的replyTo服务。
你可以看到如何提供在MessengerService.java(服务)双向消息传送和MessengerServiceActivities.java(客户端)的样品的例子。
绑定到一个服务
应用程序组件(客户端)可通过调用bindService绑定到一个服务()。然后,Android系统调用服务的onBind()方法,它返回一个IBinder与服务交互。
的结合是异步的。 bindService()立即返回和的IBinder不返回给客户端。要接收的IBinder,客户端必须创建ServiceConnection的一个实例,并将它传递给bindService()。该ServiceConnection包括系统调用来传递的IBinder的回调方法。
注意:只有活动,服务和内容提供商可以绑定到一个服务,你不能绑定到从广播接收机的服务。
因此,从客户端绑定到一个服务,你必须:
实施ServiceConnection。
你的实现必须重写两个回调方法:
onServiceConnected()
该系统调用它来提供该服务的onBind()方法返回的的IBinder。
onServiceDisconnected()
Android系统调用这个时候到服务的连接意外失去,如当服务崩溃或已被打死。当客户端解除绑定这不叫。
调用bindService(),传递ServiceConnection实施。
当系统调用你的onServiceConnected()回调方法,你就可以开始打电话到服务,使用该接口定义的方法。
从服务断开连接,调用unbindService()。
如果您的客户端被破坏,它会从服务解除绑定,但是当你做与它不被使用而当你的活动暂停,以便服务可以关闭该服务或相互作用你应该总是解除绑定。 (适当的时候,以结合并取消绑定下面讨论更多)。
例如,下面的代码片段客户端连接到通过扩展粘合剂类上面创建的服务,因此,所有必须做的是转换返回的IBinder的本地服务类,并要求用本地实例:
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// Called when the connection with the service disconnects unexpectedly
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);
bindService()的第一个参数是一个意图明确指定的服务绑定(认为的意图可能是隐含的)。
第二个参数是ServiceConnection对象。
第三个参数是指示用于结合选择的标志。它通常应该是BIND_AUTO_CREATE为了如果尚未活着创建服务。其他可能的值是无BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或0。
补充笔记
以下是关于绑定服务的一些重要事项:
你应该总是陷阱DeadObjectException例外,这是当连接已经打破抛出。这是通过远程方法抛出的唯一例外。
对象引用跨进程计数。
通常你应该配对绑定和解除绑定匹配调高客户的生命周期和拆除过程中的瞬间。例如:
如果你只需要与服务进行交互,而你的活动是可见的,你应该在onStart中绑定()和期间的onStop解除绑定()。
如果你希望你的活动来接收响应即使它是在后台停止了,那么你就可以在的onCreate绑定()和期间的onDestroy解除绑定()。要注意的是这意味着你的活动需要使用服务它的运行(即使在背景中)的整个时间,因此,如果该服务是在另一个过程中,则提高该方法的重量和它变得更可能的是,系统将杀了它。
注意:您通常不应该结合和您的活动的onResume()和的onPause()期间解除,因为这些回调发生在每一个生命周期的过渡和你应该保持发生在这些过渡到最低限度的处理。另外,如果您的应用程序绑定到同一服务的多个活动,并有其中的两个活动之间的过渡,服务可能成为下一个绑定之前破坏并重新创建作为当前活动的解除绑定(暂停期间)(恢复过程中)。 (此为活动如何协调它们的生命周期的活动转变活动文档中描述。)
欲了解更多示例代码,展示如何绑定到服务,请参阅ApiDemos的RemoteService.java类。
管理绑定服务生命周期
当服务是所有客户端绑定,Android系统破坏它(除非它也与onStartCommand启动())。因此,你不必来管理服务的生命周期,如果它是一个纯粹的绑定服务的Android系统基于它是否绑定到任何客户端管理它。
但是,如果您选择实现onStartCommand()回调方法,那么你必须明确停止服务,因为该服务现在被认为是启动。在这种情况下,服务运行,直到该服务停止本身与stopSelf()或其他组件调用stopService(),不管它是否被绑定到任何客户端。
此外,如果您的服务已启动,并接受绑定,然后当系统调用你的onUnbind()方法,您可以选择返回true,如果你想获得到onRebind()的调用,下一次客户端绑定到服务。 onRebind()返回void,但客户端仍然接收其onServiceConnected()回调的IBinder。下面,图1中示出了用于这种生命周期的逻辑。
图1为已启动,并且还允许绑定一个服务的生命周期。
有关在启动服务的生命周期的更多信息,请参阅服务文档。