一个绑定服务是服务在客户端和服务器之间的接口。一个绑定服务允许组件绑定一个服务,发送一个请求,接收响应,甚至可以执行线程间的通讯。一个绑定服务代表存活着当服务在另一个应用组件不能无限期的运行在后台。
这个文档展示你可以创建一个绑定服务,包括怎样绑定服务从另外的一个应用组件。但是,你应该参考服务文档为更多的服务信息,比如怎样样传递通知从一个服务,设置这个服务运行在前端还有更多。
基础
一个绑定服务是实现了Service类允许另外的应用绑定它并且和他交互。提供绑定服务你必须实现onBind回调方法。这个方法返回一个IBinder对象这个定义了程序接口客户端使用它和服务交互。
这个客户端绑定服务被调用bingService。当做这些时,它必须提供实现ServiceConnection,这个是连接服务的监听器,他会调用onServiceConnected在Serviceconnection中,传递IBinder这个可以用来和服务交互。
多个客户端可以同时连接服务。但是,这个系统调用服务的onBind方法恢复IBinder只有在第一个客户端绑定的时候。系统然后传递同一个IBinder给任何一个附件的客户端绑定但是不会调用onBind方法。
当最后客户端解绑从服务上,系统会销毁服务。(除非服务被startService启动)
当你实现你绑定服务,最重要的部分定义接口你onBind回调方法返回。这个是一个不同的方式你可以定义你的服务IBinder接口并且接下来部分会讨论这个技术。
创建一个绑定服务
当你创建一个服务提供绑定,你必须提供一个IBinder这个提供程序接口客户端使用和服务交互。这个有三种方式:
1. 延伸绑定类:假如你服务是你应用私有的并且运行在同一个进程中作为客户端,你必须创建你的接口用延伸的Binder类并且返回一个实例从onBind中。这个客户端接收Binder并且可以使用它直接访问公共方法在Binder实现或Service。
这个是首选技术当你服务仅仅是一个后台工作为你的应用。这个只有你不能创建接口用这个方式因为你服务被使用另外的应用或访问独立的进程。
2. 使用一个Messenger:假如你需要接口工作访问不同的进程,你可以创建一个接口为这个服务用一个Messenger。这个方式,服务定义一个Handler响应一个不同的message对象。这个Handler是基于一个Messager这个可以分享IBinder用这个客户端,允许客户端端发送命令给服务使用Message对象。另外,这个客户端可以定义一个Messenger他定义这样的服务可以发送一个message回来。
这个最简单的方式执行一个进程间的通讯,因为Messenger队列所有的请求是在一个单线程所有你不用设计服务线程安全。
3. 使用AIDL:android接口定义语言执行所有的工作分解对象到基本的单元这下可以被系统理解并且执行它们通过访问进程执行进程间的通讯。这个是最早的技术,使用一个Messenger,实际上是基于AIDL它是基础的结构。上面所提到的,Messenger创建一个队列在所有的客户端请求在一个单线程中,所以服务接收请求一次。假如但是你需要你的服务同时绑定多个请求,然后你可以使用一个AIDL。这种情况下你的服务必须有多线程的能力并且要构建线程安全。
直接使用AIDL,你必须有一个aidl文件定义在程序接口。这个androidsdk工具使用这个文件会产生一个抽象的类这个实习了接口和执行IPC,这个你可以然后扩展在你的服务里。
注意:很多的应用不需要创建一个aidl文件定义在程序接口。因为不会请求多线程的能力返回一个结果会有一个更复杂的实现。这样AIDL不是很适合许多的应用并且问到不讨论怎么使用你的服务。假如你确定需要使用AIDL,请看AIDL 文档。
扩展绑定类
假如你的服务被使用只是在本地的应用并且不需要工作访问进程,然后你可以实现Binder类提供你的客户端通过公共的方法在服务中。
注意:这个工作只有在客户端服务在这个客户端和服务在同一个应用和进程中,这个是最实用的。比如,这个工作使用与一个音乐应用需要绑定一个activity到它的服务播放音乐在后台。
这里怎样设置它:
1. 在你的服务中,创建一个Binder实例:*包含公共的方法可以让客户端调用,*返回当前的一个服务实例,这是公共方法调用*或者返回一个另外的类被服务持有带有公共的方法被客户端调用
2. 返回一个binder的实例从onBind回调方法。
3. 在客户端中,接收Binder从onServiceConnected回调方法并且调用绑定的服务使用提供的方法。
注意:这个原因服务和客户端必须在同一个应用所有客户端可以消耗返回的对象和属性调用它的API。这个服务和客户端必须在同一个进程中,因为这个技术不能执行任何编码访问进程。
比如这里有个服务提供客户端访问方法在服务通过一个Binder的实现:
public class LocalService extends Service {
// Binder given to clients
private finalIBinder mBinder =new LocalBinder();
// Random number generator
private finalRandom mGenerator = new Random();
/**
* Class used for theclient Binder. Because we know this service always
* runs in the sameprocess as its clients, we don't need to deal with IPC.
*/
public classLocalBinder extendsBinder {
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实例。这个允许客户端调用公共方法在服务中。比如客户端可以调用getRandomNujmber从服务中。
这里有个Activity绑定了一个LocalServiec和调用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和onService实现,接下来部分提供更多的信息关于这个绑定服务的进程。
注意:这个例子不能很明确的解绑服务,但是所有的客户端需解绑在一个合适的时间(比如activity暂停的时候)
为更多简单的代码,看LocalService.java类和LocalServiceActivities.java类在ApiDemos中。
使用一个Messenger
假如你需要服务交互远程的进程,然后你可以使用一个Messenger提供一个接口为你的服务。这个技术允许你执行进程的间的通讯不使用AIDL。
这里总结了怎么样使用一个Messenger:
1. 一个服务需要一个Handler这个可以接受一个回调为每个客户端
2. 一个Handler被使用来创建一个Messenger对象(这个是一个Handler的引用)
3. 这个Messenger创建一个IBinder这个服务返回重onBind。
4. 客户端使用IBinder实例化Messenger(这个引用服务的Handler),这个客户端使用这个发送一个Message对象给服务。
5. 这个服务接收每个Message在他的Handler指定在handlerMessage方法里。
用这种方式,这里没有方法为客户端调用在服务上。代替的是,客户端传送各个message对象这些服务接收在他的Handler中。
这里一个简单的服务使用一个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方法在Handler是在服务中接收内部的Message和决定做什么,基于what参数。
所有的客户端需要使用创建一个Messenger基于IBinder返回服务并且发送一个message使用send。比如,这里有个简单的activity这个绑定服务并传送一个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; } } }
注意的是这个离职不展示怎么一个服务响应客户端。假如你想这个服务响应,然后你需要创建一个Messenger在客户端中。然后当客户端接收onServiceConnected调用,他发送一个Message到一个服务这个包含了这个客户端的Messenger在replyTo参数中使用send方法。
你可以看更多的例子提供两个方式在MessengerService.java和MessengerServiceActivities.java例子中。
绑定一个服务
一个应用组件可以绑定一个服务调用bindService。这个android系统然后调用服务的onBinder方法,这个返回一个IBinder为这个服务交互。
这个绑定时异步的bindService立马返回并且不防滑一个IBinder给客户端。这个接收IBindre,客户端必须创建一个ServiceConnection实例并且传递它给bindservice。这个ServiceConnection包括方法系统调用传递一个IBinder。
注意:只有activity,service,和content provider可以绑定服务,你不能绑定服务在一个broadcast receiver。
所以绑定一个服务从你的客户端,你需要做的:
1. 实现ServiceConnection。你实现必须覆盖两个方法:
onServiceconnected:系统调用这个传递一个IBinder返回被服务的onBind方法。
onServiceDisconnected:系统调用当连接服务以外的消失,比如当服务被销毁或者被杀掉。这个不会被调用当客户端解绑的时候。
2. 调用bindService,传递ServiceConnection实现。
3. 当系统调用你的onServiceConnected回调函数,你可以开始调用这个服务使用接口定义的方法。
4. 失去连接从服务,调用unbindService。
当你客户端被销毁,他可以解绑从这个服务,但是你必须总是解绑当你需要和服务交互或者当你的activity暂停所以你的服务关闭当它被使用(合适的时间绑定和解绑在下面会讨论)
这个例子,是一个片段客户端创建扩展Binder类,所有所有必须投掷出一个IBindre返回给LocalService类并请求LocalService实例:
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);
这个第一个参数是一个Intent这个明确绑定的服务名字。
第二个参数是ServiceConnection对象。
第三个参数是一个标志位指向绑定的操作。他通常是BIND_AUTO_CREAE为了创建一个服务假如它已经挂掉。另外的值可能是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或0。
补充
这里同样很重要关于绑定一个服务:
1. 你需要中是套一个DeadObjectException,这个可以抛出当连接被破坏,这个是唯一的一个异常被远程的抛出。
2. 对象应用计数访问进程
3. 你通常把绑定和解绑弄成一对在合适的客户端生命周期中。
比如:假如你需要交互和服务在你的activity可见,你应该绑定在onStart和解绑在onStop。
假如你想你的activity接收响应即使在后台他停止,然后你绑定在onCreate并且解绑在onDestroy。注意这个暗示你的activity需要使用服务在整个运行期间,所有假如服务在另外一个进程,然后你增加这个进程的权重并且他变的系统会杀掉它。
注意你通常不会绑定和解绑在activity的onResum和onPause,因为这个回调发送在任何的事务周期并且你需要保持进程发生这些事务最小。也就是,假如多个activity在你的应用绑定同一个服务并且这里一个服务在两个activity中,这个服务销毁或创建在当前activity解绑的时候再下一个绑定。(activity事务怎么样协调在他们的生命周期在activity文档中描述)
更多的代码,展示怎么样绑定一个服务,看RemoteService.java类在ApiDemos。
管理一个绑定服务的生命周期
当一个服务解绑所有的客户端,android系统销毁它(除非他用onStartCommand启动)。就这样,你不能管理你服务的生命周期假如它是一个绑定服务,android系统管理他是建立在你是否绑定多个客户端。
但是假如你选择实现onStartOnmmand调用方法,然后你必须明确停止这个服务,因为你服务不启动了。这种情况下,你的服务运行直到服务停止用它的stopSelf或者另外的组件调用stopService,不管是否它绑定在多个客户端中。
还有,假如你的服务被启动并且接受绑定,然后系统调用onUnBind方法,你可以返回true假如你想接受一个onRebind在下次客户端绑定服务的时候(代替调用onBind).onRebind返回一个void,但是客户端仍然可以接收到IBinder在他的onServiceConnected回调中。下面是这些生命周期的逻辑。