android基础部分再学习---再谈Service进程服务通信
Bound Services
RemoteService LocalService</div> </div>bound服务是客户端-服务器模式的服务。bound服务允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。 bound服务一般只在为其它应用程序组件服务期间才是存活的,而不会一直在后台保持运行。
本文展示了如何创建一个bound服务,包括如何从其它应用程序组件绑定到该服务。不过,通常你还应该参考服务文档以获取关于服务的更多信息,比如如何从服务中发送通知、如何将服务设置为前台运行等等。
目录</div>
|
简介
bound服务是Service类的一种实现,它允许其它应用程序与其绑定并交互。为了让服务支持绑定,你必须实现onBind()回调方法。这个方法返回一个IBinder对象,此对象定义了客户端与服务进行交互时所需的编程接口。
绑定到一个started服务正如服务一文中所述,你可以创建一个同时支持started和bound的服务。也就是说,服务可以通过调用startService()来启动,这使它一直保持运行,同时它也允许客户端通过调用bindService()来与之绑定。
如果你的服务确实可以是started和bound的,那么服务启动后,系统将不会在所有客户端解除绑定时销毁它。取而代之的是,你必须通过调用stopSelf()或stopService()显式终止此服务。
虽然你通常应该要实现onBind()或onStartCommand()中的一个,但有时需要同时实现两者。比如,音乐播放器的服务也许就需要同时实现后台运行和支持绑定。这样,activity就可以启动服务来播放音乐,并且音乐会一直播放下去,即使用户离开该应用程序也没关系,这个activity可以绑定播放服务来重新获得播放控制权。
请确保已经阅读了#管理Bound服务的生命周期章节,以获取更多向started服务添加绑定时的服务生命周期的有关信息。
</div>客户端可以通过调用bindService()方法来绑定服务。在调用时,必须提供一个ServiceConnection的实现代码,用于监控与服务的联接。bindService()将会立即返回,没有返回值。但是Android系统在创建客户端与服务之间的联接时,会调用ServiceConnection中的onServiceConnected()方法,传递一个IBinder,客户端将用它与服务进行通信。
多个客户端可以同时联接到一个服务上。不过,只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来获取IBinder。然后,系统会向后续请求绑定的客户端传送这同一个IBinder,而不再调用onBind()。
当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过startService()启动的)。
当你实现自己的bound服务时,最重要的工作就是定义onBind()回调方法所返回的接口。定义服务IBinder接口的方式有好几种,后续章节将会对每种技术进行论述。
创建一个Bound服务
创建一个支持绑定的服务时,你必须提供一个IBinder,用作客户端和服务间进行通信的编程接口。定义这类接口的方式有三种:
扩展Binder类 如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过扩展Binder类来创建你的接口,并从onBind()返回一个它的实例。客户端接收该Binder对象并用它来直接访问Binder甚至Service中可用的公共(public)方法。 如果你的服务只是为你自己的应用程序执行一些后台工作,那这就是首选的技术方案。不用这种方式来创建接口的理由只有一个,就是服务要被其它应用程序使用或者要跨多个进程使用。 使用Messenger 如果你需要接口跨越多个进程进行工作,可以通过Messenger来为服务创建接口。在这种方式下,服务定义一个响应各类消息对象Message的Handler。此Handler是Messenger与客户端共享同一个IBinder的基础,它使得客户端可以用消息对象Message向服务发送指令。此外,客户端还可以定义自己的Message,以便服务能够往回发送消息。 这是执行进程间通信(IPC)最为简便的方式,因为Messenger会把所有的请求放入一个独立进程中的队列,这样你就不一定非要把服务设计为线程安全的模式了。 使用AIDL Android接口定义语言AIDL(Android Interface Definition Language)完成以下的所有工作:将对象解析为操作系统可识别的原始形态,并将它们跨进程序列化(marshal)以完成IPC。前一个使用Messenger的方式,实际上也是基于AIDL的,它用AIDL作为底层结构。如上所述,Messenger将在一个单独的进程中创建一个包含了所有客户端请求的队列,这样服务每次就只会收到一个请求。可是,如果想让你的服务能同时处理多个请求,那你就可以直接使用AIDL。这种情况下,你的服务必须拥有多线程处理能力,并且是以线程安全的方式编写的。 要直接使用AIDL,你必须创建一个.aidl文件,其中定义了编程的接口。Android SDK 工具使用此文件来生成一个抽象类(abstract class),其中实现了接口及对IPC的处理,然后你就可以在自己的服务中扩展该类。注意:绝大多数应用程序都不应该用AIDL来创建bound服务,因为这可能需要多线程处理能力并且会让代码变得更为复杂。 因此,AIDL对绝大多数应用程序都不适用,并且本文也不会讨论如何在服务中使用它的内容。如果你确信需要直接使用AIDL,那请参阅AIDL文档。
</div>扩展Binder类
如果你的服务只用于本地应用程序并且不需要跨进程工作,那你只要实现自己的Binder类即可,这样你的客户端就能直接访问服务中的公共方法了。
注意:仅当客户端和服务位于同一个应用程序和进程中,这也是最常见的情况,这种方式才会有用。比如,一个音乐应用需要把一个activity绑定到它自己的后台音乐播放服务上,采用这种方式就会很不错。
</div>以下是设置步骤:
在你的服务中,创建一个Binder的实例,其中实现以下三者之一: 包含了可供客户端调用的公共方法 返回当前Service实例,其中包含了可供客户端调用的公共方法。 或者,返回内含服务类的其它类的一个实例,服务中包含了可供客户端调用的公共方法。 从回调方法onBind()中返回Binder的该实例。 在客户端中,在回调方法onServiceConnected()中接收Binder并用所提供的方法对绑定的服务进行调用。服务和客户端之所以必须位于同一个应用程序中,是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。 服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。
</div>比如,以下是一个服务的示例,它通过实现一个Binder来为客户端访问它内部的方法提供支持:
publicclassLocalServiceextendsService{
// 给客户端的Binder
privatefinalIBindermBinder=newLocalBinder();
// 生成随机数
privatefinalRandommGenerator=newRandom();
/**
* 用于客户端Binder的类。
* 因为知道本服务总是运行于与客户端相同的进程中,我们就不需要用IPC进行处理。
*/
publicclassLocalBinderextendsBinder{
LocalServicegetService(){
// Return this instance of LocalService so clients can call public methods
returnLocalService.this;
}
}
@Override
publicIBinderonBind(Intentintent){
returnmBinder;
}
/** method for clients */
publicintgetRandomNumber(){
returnmGenerator.nextInt(100);
}
}
LocalBinder为客户端提供了getService()方法,用于返回当前LocalService的实例。 这就让客户端可以调用服务中的公共方法。比如,客户端可以调用服务中的getRandomNumber()。
以下是一个绑定到LocalService的activity,当点击按钮时,它会调用getRandomNumber():
publicclassBindingActivityextendsActivity{
LocalServicemService;
booleanmBound=false;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protectedvoidonStart(){
super.onStart();
// 绑定到LocalService
Intentintent=newIntent(this,LocalService.class);
bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
}
@Override
protectedvoidonStop(){
super.onStop();
// 与服务解除绑定
if(mBound){
unbindService(mConnection);
mBound=false;
}
}
/** 当按下按