Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1085715
  • 博文数量: 169
  • 博客积分: 12306
  • 博客等级: 上将
  • 技术积分: 1299
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-29 14:55
文章分类

全部博文(169)

文章存档

2012年(18)

2011年(78)

2010年(15)

2009年(1)

2008年(11)

2007年(39)

2006年(7)

我的朋友

分类: LINUX

2011-10-20 13:56:34

Bound Services

一个绑定的服务是一个客户端之间的接口的服务。一个绑定的服务器允许组件(比如 Activity)绑定到这个服务,发送请求,接收响应,甚至执行进程间通信。一个绑定的服务典型的是只当他为别的组件服务时才是活动的 ,他不会在后台单独运行。

 

基础知识 (The Basics)

一个绑定的服务是一个 Service类的一个实现,他允许别的应用绑定到他,并且与他交互。一个服务要实现绑定功能,你必须实现 onBind()回调方法。这个方法返回一个 IBinder的对象,他定义了客户端可以用来与服务相交互的接口。

一个客户端可以通过 bindService()绑定到这个服务。当他这样做的时候,他们必须提供一个 ServiceConnection的实现,用他来监视与该服务的连接。 bindService()会立即返回,且而任何返回值。但是当 Android系统创建客户端与服务端的连接时,他会调用 ServiceConnection onServiceConnected()方法,并且给他传递 IBinder对象,客户端可以用他来与服务端进行通信。

多个客户端可以同时绑定到这个服务。然而,系统只在第一次客户端绑定到该服务时调用该服务的 onBind()方法去得到 IBinder。系统将传递相同的 IBinder到任何其它的绑定到该服务的客户端,而不是再调用 onBind()一次。

当最后一个客户端解除了与服务的绑定,系统将销毁掉这个服务。 (如果该服务也被 startService()启动除外。 )

当你实现你的绑定的服务时,最重要的工作是定义你的 onBind()方法返回的接口。

 

创建一个绑定服务 (Creating a Bound Service)

当创建一个服务,提供绑定功能,你必须提供一个 IBinder作为与客户端进行交互的接口。这里有三种方法你可以用来定义这个接口。

扩展 Binder (Extending the Binder Class)

如果你的服务对于你的应用而言是私有的并且与这个客户端运行在同一个进程中 (这是一种很常见的 ),你应该通过扩展 Binder来创建你的接口,并通过 onBind()来返回这个接口。客户端得到 Binder,并且可以直接该问他的公有方法,甚至服务的公有方法。

当服务仅仅作为你自己应用中后台运行的一部分时,使用这个技术是比较好的选择。唯一不应该使用这种方法的理由是,你的服务可能会被其它应用使用,或者从不同的进程访问。

使用 Messenger(Using Messenger)

如果你的服务需要跨进程工作,你可以使用 Messenger为你的服务创建一个接口。这种方式下,服务定义一个 Handler,他来响应不同类型的 Message对象。这个 Handler Messenger的一个基础,他可以给客户端分享一个 IBinder,他允许客户端使用 Message对象向该服务发命令。另外,客户端可以为自己定义一个 Messenger,这样服务端可以发消息回来。

这是一种进程间通信 (IPC)的最简单的方式。由于 Messenger将所有的请求排好队放入一个单独的线程,所以你永远不需要设计你的服务为线程安全。

使用 AIDL(Using AIDL)

AIDL(Android Interface Definition Language) 执行所有的工作来将对象分解为操作系统可以理解的原始状态并调整它们来跨线程执行 IPC。前面的技术,使用 Messenger,实际上是基于 AIDL作为基础架构的。像前面提到的那样, Messenger一个单独的线程中为所有的客户端的请求创建一个队列,所以一次只得到一个请求。然而,如果你想要你的服务同时处理多个请求,你可以直接使用 AIDL。这种情况下,你的服务必须具备多线程能力,并且要考虑线程安全。

直接使用 AIDL,你必须定义一个 .aidl文件来定义编程接口。 Android SDK工具用这个文件去生成一个抽象类,他来实现接口和处理 IPC,这些可以在你的服务中被扩展。

注:绝大多数应用都不应该使用 AIDL来创建绑定服务,因为他需要多线程能力,且使其实现复习化。

扩展 Binder (Extending the Binder class)

如果你的服务只被你的本地服务使用,不需要跨进程的工作。你可以实现你的 Binder类,使你的客户端可以直接访问你的服务的公有方法。

步骤:

1.       在你的服务中,创建一个 Binder的实例,

a.       它包括客户可以访问的公共方法

b.      返回当前服务的实例,他有一些公有方法可供客户端调用

c.       或者,返回一个别的类的实例,这个类持有可供客户调用的一些公共方法的服务。

2.       onBind()方法返回一个 Binder的实例。

3.       在客户端,从 onServiceConnected()获得一个 Binder,并使用服务提供了的方法去调用绑定服务。

倒如,这个服务通过 Binder的实现提供了客户端访问服务的一些方法。

 

  1. public class LocalService extends Service {  
  2.     // Binder given to clients  
  3.     private final IBinder mBinder = new LocalBinder();  
  4.     // Random number generator  
  5.     private final Random mGenerator = new Random();  
  6.     /** 
  7.      * Class used for the client Binder.  Because we know this service always 
  8.      * runs in the same process as its clients, we don't need to deal with IPC. 
  9.      */  
  10.     public class LocalBinder extends Binder {  
  11.         LocalService getService() {  
  12.             // Return this instance of LocalService so clients can call public methods  
  13.             return LocalService.this;  
  14.         }  
  15.     }  
  16.     @Override  
  17.     public IBinder onBind(Intent intent) {  
  18.         return mBinder;  
  19.     }  
  20.     /** method for clients */  
  21.     public int getRandomNumber() {  
  22.       return mGenerator.nextInt(100);  
  23.     }  
  24. }  
 

 

LocalBinder为客户端提供了一个 getService()方法,可以用来获取当前的 LocalService的实例。他允许客户端调用服务的公共方法。例如,客户端可以调用服务的 getRandomNumber()方法。

这里有一个 Activity绑定到这个服务,并且当点击一次按钮,调用一次 getRandomNumber()方法。

 

  1. public class BindingActivity extends Activity {  
  2.     LocalService mService;  
  3.     boolean mBound = false;  
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.main);  
  8.     }  
  9.     @Override  
  10.     protected void onStart() {  
  11.         super.onStart();  
  12.         // Bind to LocalService  
  13.         Intent intent = new Intent(this, LocalService.class);  
  14.         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
  15.     }  
  16.     @Override  
  17.     protected void onStop() {  
  18.         super.onStop();  
  19.         // Unbind from the service  
  20.         if (mBound) {  
  21.             unbindService(mConnection);  
  22.             mBound = false;  
  23.         }  
  24.     }  
  25.     /** Called when a button is clicked (the button in the layout file attaches to 
  26.       * this method with the android:onClick attribute) */  
  27.     public void onButtonClick(View v) {  
  28.         if (mBound) {  
  29.             // Call a method from the LocalService.  
  30.             // However, if this call were something that might hang, then this request should  
  31.             // occur in a separate thread to avoid slowing down the activity performance.  
  32.             int num = mService.getRandomNumber();  
  33.             Toast.makeText(this"number: " + num, Toast.LENGTH_SHORT).show();  
  34.         }  
  35.     }  
  36.     /** Defines callbacks for service binding, passed to bindService() */  
  37.     private ServiceConnection mConnection = new ServiceConnection() {  
  38.         @Override  
  39.         public void onServiceConnected(ComponentName className,  
  40.                 IBinder service) {  
  41.             // We've bound to LocalService, cast the IBinder and get LocalService instance  
  42.             LocalBinder binder = (LocalBinder) service;  
  43.             mService = binder.getService();  
  44.             mBound = true;  
  45.         }  
  46.         @Override  
  47.         public void onServiceDisconnected(ComponentName arg0) {  
  48.             mBound = false;  
  49.         }  
  50.     };  
  51. }  
 

以上实例显示了一个客户端如何使用一个 ServiceConnection onServiceConnected()回调方法绑定到这个服务。

 

使用 Messenger(Using a Messenger)

如果你的服务需要远程通信,你可以使用一个 Messenger为你的服务提供接口。这个技术允许你不需要 AIDL的情况下执行进程间通信( IPC)。

下面是使用 Messenger的总结:

·         服务实现一个 Handler,为每一个来自客户端的调用接收一个回调。

·         Handler用来创建 Messenger实体 (他是一个到 Handler的引用 )

·         Messenger创建一个 IBinder,用来作为服务的 onBind()的返回值。

·         客户端使用 IBinder来初始化一个 Messenger(他是到服务的 Handler的一个引用 ),客户端用他来发送 Message到服务。

·         服务在他 Handler中接收每一个 Message,特别的,在 handleMessage()方法中。

用这种方法,服务没有为客户端提供调用的方法。相反,客户端传递 message,服务在 Handler中接收他。

下面是一个使用 Messengar的例子:

  1. public class MessengerService extends Service {  
  2.     /** Command to the service to display a message */  
  3.     static final int MSG_SAY_HELLO = 1;  
  4.     /** 
  5.      * Handler of incoming messages from clients. 
  6.      */  
  7.     class IncomingHandler extends Handler {  
  8.         @Override  
  9.         public void handleMessage(Message msg) {  
  10.             switch (msg.what) {  
  11.                 case MSG_SAY_HELLO:  
  12.                     Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();  
  13.                     break;  
  14.                 default:  
  15.                     super.handleMessage(msg);  
  16.             }  
  17.         }  
  18.     }  
  19.     /** 
  20.      * Target we publish for clients to send messages to IncomingHandler. 
  21.      */  
  22.     final Messenger mMessenger = new Messenger(new IncomingHandler());  
  23.     /** 
  24.      * When binding to the service, we return an interface to our messenger 
  25.      * for sending messages to the service. 
  26.      */  
  27.     @Override  
  28.     public IBinder onBind(Intent intent) {  
  29.         Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();  
  30.         return mMessenger.getBinder();  
  31.     }  
  32. }  

 

注意 Handler handleMessage()方法,他是服务接收消息的地方,并且决定去做什么,基于 what成员。

客户端需要做的所有工作是创建一个基于从服务返回的 IBinder Messenger,然后使用 send()发送一个消息 (message)。下面示例一个 activity绑定到这个服务,并且向服务传递 MSG_SAY_HELLO消息。

  1. public class ActivityMessenger extends Activity {  
  2.     /** Messenger for communicating with the service. */  
  3.     Messenger mService = null;  
  4.     /** Flag indicating whether we have called bind on the service. */  
  5.     boolean mBound;  
  6.     /** 
  7.      * Class for interacting with the main interface of the service. 
  8.      */  
  9.     private ServiceConnection mConnection = new ServiceConnection() {  
  10.         public void onServiceConnected(ComponentName className, IBinder service) {  
  11.             // This is called when the connection with the service has been  
  12.             // established, giving us the object we can use to  
  13.             // interact with the service.  We are communicating with the  
  14.             // service using a Messenger, so here we get a client-side  
  15.             // representation of that from the raw IBinder object.  
  16.             mService = new Messenger(service);  
  17.             mBound = true;  
  18.         }  
  19.         public void onServiceDisconnected(ComponentName className) {  
  20.             // This is called when the connection with the service has been  
  21.             // unexpectedly disconnected -- that is, its process crashed.  
  22.             mService = null;  
  23.             mBound = false;  
  24.         }  
  25.     };  
  26.     public void sayHello(View v) {  
  27.         if (!mBound) return;  
  28.         // Create and send a message to the service, using a supported 'what' value  
  29.         Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 00);  
  30.         try {  
  31.             mService.send(msg);  
  32.         } catch (RemoteException e) {  
  33.             e.printStackTrace();  
  34.         }  
  35.     }  
  36.     @Override  
  37.     protected void onCreate(Bundle savedInstanceState) {  
  38.         super.onCreate(savedInstanceState);  
  39.         setContentView(R.layout.main);  
  40.     }  
  41.     @Override  
  42.     protected void onStart() {  
  43.         super.onStart();  
  44.         // Bind to the service  
  45.         bindService(new Intent(this, MessengerService.class), mConnection,  
  46.             Context.BIND_AUTO_CREATE);  
  47.     }  
  48.     @Override  
  49.     protected void onStop() {  
  50.         super.onStop();  
  51.         // Unbind from the service  
  52.         if (mBound) {  
  53.             unbindService(mConnection);  
  54.             mBound = false;  
  55.         }  
  56.     }  
  57. }  
 

 

注意,这个示例没有显示服务怎样才能响应到客户端。如果你想要你的服务响应,你也需要在你的客户端中创建 一个 Messenger。然后当客户端接到 onServiceConnected()回调,他发送一个 Message到服务,他在 replyTo参数的 send()方法中包括客户端的 Messenger

 

绑定到一个服务 (Binding to a Service)

应用组件可以通过 bindService()绑定到一个服务。 Android系统然后调用系统的 onBind()方法,他返回一个 IBinder以供与服务交互。

绑定是异步的。 bindService()会立即返回,并不返回 IBinder到客户端。为了接收 IBinder,客户端必须创建一个 ServiceConnection的实例,并将他传递给 bindService()方法。 ServiceConnection包括一个回调方法,系统可以用来交付 IBinder

注:只有 Activity Service ,和 Content Provider 可以绑定到服务,你不能从广播接收者 (Broadcast receiver) 来绑定服务。

所以,从客户端绑定一个服务,你需要:

1.       实现一个 ServiceConnection

你的实现必须重载以下两个方法 :

onServiceConnected()

        系统调用这个方法传递 onBind()返回的 IBinder

onServiceDisconnected()
        当与服务的连接被异常中断时 Android系统会调用这个方法,例如当服务崩溃 (crash)了或者被杀死 (kill)了。当客户端调用 unBind()时系统是不会调用该方法( onServiceDisconnected() )。

2.       调用 BindService(),传递 ServiceConnection的实现。

3.       当系统调用你的 onServiceConnected()方法时,你可以使用接口定义了的方法向服务做出一个调用。

4.       断开与一个服务的连接,使用 unBindService()

当你的客户端被销毁了,他将与服务解除绑定关系。但你总是应该在完成与服务的交互之后来解除这种绑定,或者当你的 Activity处于 Pause状态时,当服务不在使用时应该关闭。

 

例如,以下代码片断连接一个客户端到一个扩展 Binder类,所以需要做的是获取一个 IBinder LoacalService类并且请求 LocalService的实例。

  1. LocalService mService;  
  2. private ServiceConnection mConnection = new ServiceConnection() {  
  3.     // Called when the connection with the service is established  
  4.     public void onServiceConnected(ComponentName className, IBinder service) {  
  5.         // Because we have bound to an explicit  
  6.         // service that is running in our own process, we can  
  7.         // cast its IBinder to a concrete class and directly access it.  
  8.         LocalBinder binder = (LocalBinder) service;  
  9.         mService = binder.getService();  
  10.         mBound = true;  
  11.     }  
  12.     // Called when the connection with the service disconnects unexpectedly  
  13.     public void onServiceDisconnected(ComponentName className) {  
  14.         Log.e(TAG, "onServiceDisconnected");  
  15.         mBound = false;  
  16.     }  
  17. };  
 

 

使用这个 ServiceConnection,客户端可以传递他给 bindService()来绑定服务。例如:

  1. Intent intent = new Intent(this, LocalService.class);  
  2. bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
 

 

bindService()的第一个参数是一个 intent,他显式地指明了要绑定的服务(尽管他可以是隐式的)。

第二个参数是这个 ServiceConnection对象。

第三个参数是一个标志,他指明的绑定的选项。他通常应该是 BIND_AUTO_CREATE,如果这个服务不是活动的将会创建他。别的可能的值有 BIND_DEBUG_UNBIND BIND_NOT_FORGOUND,或者是 0表示什么也没有。

  其它注意事项 (Additional notes)

这里有关于绑定到服务的一些比较重要的提示:

你总是应该要注意捕捉 DeadObjectException异常,当连接被中断了会抛出这个异常。这是远程方法中唯一抛出的异常。

对象是跨进程计数器。

你应该让你的绑定和解除绑定在你的客户端的生命周期的启动与销毁间配对。例如:

如果你仅需要在你的 Activity可见期间与服务通信,你应该在 onStart()中绑定他,在 onStop()中解除绑定。

如果你的 Activity在停止之后还接收服务的响应,那你应该在 onCreate()中绑定他,在 onDestroy()中解除这种绑定。

注:通常你不应该在 onResume()中绑定和在 onPause()中解除绑定。因为这两个回调在每一个生命周期的转换中都会发生,你应该保持这种处理的最小化。

管理一个绑定服务的生命周期 (Managing the Lifecycle of a Bound Service)

当一个服务与所有的客户解除绑定了, Android系统将销毁他 (他也被 onStartCommand()启动除外 )。因此,如果你的服务是一个纯 bind的服务,你不需要管理你的服务的生命周期。 Android系统将基于他是否绑定到客户端而为你管理他。

然而,如果你的选择了实现 onStartCommand()的回调,你必须显示的停止掉你的服务,因为现在系统认为你的服务是 started的。这种情况下,服务运行到自己调用 stopSelf()或者其它组件调用 stopService(),不论他是否绑定到客户端。

如果你的服务既是 started,也接受绑定,当系统调用你的 onUnbind()方法,如果你更希望下次客户端绑定到服务时接到 onRebind()的调用,你可以选择性的返回 true onRebind()返回一个 void,但是客户端仍然可以在 onServiceConnected()中接到 IBinder对象。


原文地址:http://blog.csdn.net/mshopping/article/details/6537221

阅读(2133) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~