android基础部分再学习---再谈Service进程服务通信
Posted wust小吴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android基础部分再学习---再谈Service进程服务通信相关的知识,希望对你有一定的参考价值。
Bound Services
- 快速查看
- bound服务允许被其它控件绑定,以便与之交互并进行进程间通信
- 一旦所有的客户端都解除了绑定,bound服务将被销毁。除非该服务同时又是started类型的。
- 在本文中(参见目录)
- 关键类
bound服务是客户端-服务器模式的服务。bound服务允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。 bound服务一般只在为其它应用程序组件服务期间才是存活的,而不会一直在后台保持运行。
本文展示了如何创建一个bound服务,包括如何从其它应用程序组件绑定到该服务。不过,通常你还应该参考服务文档以获取关于服务的更多信息,比如如何从服务中发送通知、如何将服务设置为前台运行等等。
目录 |
简介
bound服务是 Service 类的一种实现,它允许其它应用程序与其绑定并交互。为了让服务支持绑定,你必须实现onBind() 回调方法。这个方法返回一个 IBinder 对象,此对象定义了客户端与服务进行交互时所需的编程接口。
- 绑定到一个started服务
正如服务一文中所述,你可以创建一个同时支持started和bound的服务。也就是说,服务可以通过调用 startService() 来启动,这使它一直保持运行,同时它也允许客户端通过调用 bindService()来与之绑定。
如果你的服务确实可以是started和bound的,那么服务启动后,系统将不会在所有客户端解除绑定时销毁它。取而代之的是,你必须通过调用stopSelf() 或 stopService() 显式终止此服务。
虽然你通常应该要实现 onBind() 或onStartCommand() 中的一个,但有时需要同时实现两者。比如,音乐播放器的服务也许就需要同时实现后台运行和支持绑定。这样,activity就可以启动服务来播放音乐,并且音乐会一直播放下去,即使用户离开该应用程序也没关系,这个activity可以绑定播放服务来重新获得播放控制权。
请确保已经阅读了#管理Bound服务的生命周期章节,以获取更多向started服务添加绑定时的服务生命周期的有关信息。
客户端可以通过调用 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 文档。
扩展Binder类
如果你的服务只用于本地应用程序并且不需要跨进程工作,那你只要实现自己的 Binder 类即可,这样你的客户端就能直接访问服务中的公共方法了。
注意:仅当客户端和服务位于同一个应用程序和进程中,这也是最常见的情况,这种方式才会有用。比如,一个音乐应用需要把一个activity绑定到它自己的后台音乐播放服务上,采用这种方式就会很不错。
以下是设置步骤:
- 在你的服务中,创建一个Binder的实例,其中实现以下三者之一:
- 包含了可供客户端调用的公共方法
- 返回当前Service实例,其中包含了可供客户端调用的公共方法。
- 或者,返回内含服务类的其它类的一个实例,服务中包含了可供客户端调用的公共方法。
- 从回调方法onBind()中返回Binder的该实例。
- 在客户端中,在回调方法onServiceConnected()中接收Binder并用所提供的方法对绑定的服务进行调用。
服务和客户端之所以必须位于同一个应用程序中,是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。 服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。
比如,以下是一个服务的示例,它通过实现一个Binder来为客户端访问它内部的方法提供支持:
public class LocalService extends Service {
// 给客户端的Binder
private final IBinder mBinder = new LocalBinder();
// 生成随机数
private final Random mGenerator = new Random();
/**
* 用于客户端Binder的类。
* 因为知道本服务总是运行于与客户端相同的进程中,我们就不需要用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()方法,用于返回当前LocalService的实例。 这就让客户端可以调用服务中的公共方法。比如,客户端可以调用服务中的getRandomNumber()。
以下是一个绑定到LocalService的activity,当点击按钮时,它会调用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();
// 绑定到LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 与服务解除绑定
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** 当按下按钮时调用(该按钮在layout文件中利用android:onClick属性与本方法关联 */
public void onButtonClick(View v) {
if (mBound) {
// 调用LocalService中的方法。
// 不过,如果该调用会导致某些操作的挂起,那么调用应该放入单独的线程中进行,
// 以免降低activity的性能。
int num = mService.getRandomNumber();
Toast.makeText(this, "number:
" + num, Toast.LENGTH_SHORT).show();
}
}
/** 定义服务绑定时的回调方法,用于传给bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// 我们已经绑定到LocalService了,对IBinder进行类型转换(cast)并获得LocalService对象的实例
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
上述例子展示了客户端如何利用 ServiceConnection 和 onServiceConnected() 回调方法绑定到服务。下一节将给出更多有关服务绑定过程的信息。
上述例子并没有明确地解除绑定,但所有的客户端都应该适时地解除绑定(比如activity暂停pause时)。
更多示例代码,请参阅ApiDemos 中的LocalService.java类和 LocalServiceActivities.java 类。
使用Messenger
- 与AIDL相比
当你需要进行IPC时,使用 Messenger 要比用AIDL实现接口要容易些,因为 Messenger 会把所有调用服务的请求放入一个队列。而纯粹的AIDL接口会把这些请求同时发送给服务,这样服务就必须要能够多线程运行。
对于绝大多数应用程序而言,服务没有必要多线程运行,因此利用 Messenger 可以让服务一次只处理一个调用。如果 你的服务非要多线程运行,那你就应该用 AIDL 来定义接口。
如果你的服务需要与远程进程进行通信,那你可以使用一个 Messenger 来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC)。
以下概括了Messenger的使用方法:
- 服务实现一个Handler ,用于客户端每次调用时接收回调。
- 此Handler用于创建一个Messenger对象(它是一个对Handler的引用)。
- 此Messenger对象创建一个IBinder,服务在onBind()中把它返回给客户端。
- 客户端用IBinder将Messenger(引用服务的Handler)实例化,客户端用它向服务发送消息对象Message。
- 服务接收Handler中的每个消息Message——确切的说,是在handleMessage()方法中接收。
通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”( Message对象),服务则接收位于 Handler中的这个消息。
以下是服务使用一个Messenger做为接口的简单例子:
public class MessengerService extends Service {
/** 发送给服务的用于显示信息的指令*/
static final int MSG_SAY_HELLO = 1;
/**
* 从客户端接收消息的Handler
*/
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);
}
}
}
/**
* 向客户端公布的用于向IncomingHandler发送信息的Messager
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* 当绑定到服务时,我们向Messager返回接口,
* 用于向服务发送消息
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.以上是关于android基础部分再学习---再谈Service进程服务通信的主要内容,如果未能解决你的问题,请参考以下文章