Android开发向导---Bound Service(译文)

绑定的服务(Bound Service)

一个绑定的服务是C/S中的server。一个绑定的服务允许组件绑定服务、发送请求、接受响应,甚至IPC通信。典型地,一个绑定的服务仅在为其他程序组件服务时才存在并且不会无限期的在后台运行。

       这个文档展示了如何创建绑定的服务,包括如何从其他应用程序组件中绑定服务。然而,你也可以引用Services闻到那股获得其他关于服务的额外信息,比如如何从一个服务中传递通知,在前台设置运行服务等等。

 

基础

一个已绑定的服务是一个Service类的实现,这个类可以与其他Application绑定并与之交互。为了提供一个服务的绑定,你必须实现回调方法onBind()。这个方法返回实现IBinder接口的对象,IBinder接口定义了客户端用于交互的接口。

       一个客户端调用bindService()绑定服务,参数提供一个ServiceConnection的实现,用于监视服务的连接,bindService()会立即返回,但当Android系统创建客户端和服务的连接时,会调用ServiceConnection接口中的onServiceConnected()来传递IBinder。

     多个客户端可同时连接一个服务。然而第一个客户端绑定时系统调用你的服务中的onBind()方法来获得Ibinder。然后系统将同一个IBinder传递给其后绑定的所有客户端,而不会再调用onBind()方法。

       当最后一个客户端的解除绑定时,系统会销毁这个服务(除非服务也通过StartService()启动过)。

       实现了你绑定的服务,最重要的是定义OnBind()回调方法返回的IBinder接口对象。下面介绍定义一个服务的Ibinder接口的几种方法。

 

创建服务

创建服务时,你必须实现Ibinder接口,该接口提供了客户端s与服务交互的编程接口。有三种方法:

1)继承Binder类

      如果服务是专为(private)一个应用程序服务的,且与客户端运行在同一个进程中,你需要通过继承Binder类来创建接口,同时在onBind()中返回它。客户端接收到这个Binder类,用它直接访问Binder实现的甚至Service中实现的public方法。

       当你的服务仅仅是其应用程序的后台workers时,这是一个首选技术。你不会使用这种方法创建一个接口的唯一原因是你的service被其他的应用程序使用或者使用了单独的进程。

2)使用Messenger

      如果你需要跨进程运行,利用Messenger可以为服务创建一个接口。这种方式下,服务定义一个Handler响应不同类型的Message。这个Handler是用于与客户端共享的Messenger的基础,允许客户端利用Message发送命令给服务。另外客户端定义了一个它自己的Messenger所以服务可以送回消息。

      这是实现IPC的最简单的方式,因为Messenger队列化所有的请求到单个线程中,以至于你不必设计自己的服务是线程安全的。

3)使用AIDL

      AIDL (Android Interface Definition Language,Android接口描述语言)执行所有的工作来分解对象为操作系统能理解的原语并安排它们跨进程地执行IPC。上一个使用Messenger的方法实际上也是基于AIDL作为它的底层结构。上面提到,Messenger在单个线程中创建所有客户端s的队列,所以服务一次依次接受请求。但如果你需要同时处理多个请求,你需要直接使用AIDL。在这种情况下,服务必须是多线程和线程安全的。

      为了直接使用AIDL,必须创建一个.aidl的文件来定义编程接口。Android SDK工具使用这个文件产生一个实现了这些接口和处理IPC的抽象类,然后你可以在你的服务中继承它。

      注:大部分的应用程序不应该使用AIDL创建服务,因为它需要多线程,导致了更复杂的实现。同样地,AIDL不适合大部分的应用程序,本文档不讨论如何使用它。如果你需要,请参见AIDL文档。

继承Binder类

如果你的服务仅仅用于本地应用而且无需跨进程,你可以实现自己的Binder使客户端可直接访问服务中的public方法。

      实现步骤:

      1. 在你的服务中,创建Binder的实例,它需要以下方面中的一个:

            a. 包含客户端可调用的public方法

            b. 返回当前Service的实例,其包含客户端可调用的public方法

            c. 返回另一个类的实例,其由Service支持,包含客户端可调用的public方法

      2. 在onBind()中返回Binder实例

      3. 在客户端中,接受来自回调函数onServiceConnected()的Binder对象,并使用提供的方法访问服务

       下面是通过Binder实现一个可被客户端访问的服务。  

View Code
 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     /**
 8      * Class used for the client Binder.  Because we know this service always
 9      * runs in the same process as its clients, we don't need to deal with IPC.
10      */
11     public class LocalBinder extends Binder {
12         LocalService getService() {
13             // Return this instance of LocalService so clients can call public methods
14             return LocalService.this;
15         }
16     }
17 
18     @Override
19     public IBinder onBind(Intent intent) {
20         return mBinder;
21     }
22 
23     /** method for clients */
24     public int getRandomNumber() {
25       return mGenerator.nextInt(100);
26     }
27 }

  LocalBinder 提供方法getService()用于客户端s获得LocalService实例。这允许客户端s访问服务中的public方法。例如getRandomNumber()。

      下面一个活动(Activity)绑定了LocalService,并在按钮被点击时调用getRandomNumber()。  

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

  上面的例子展示了客户端如何利用ServiceConnection和回调onServiceConnected()来绑定服务。下一节提供了绑定服务的进程的更多信息。

      注:该例没有明确地解绑(unbind)服务,但是所有的客户端应该在合适的时间(例如活动暂停时)解绑服务。

      更多的实例代码,查看在ApiDemons中的LocalService.java和LocalServiceActivities.java。

使用Messenger

如果你的服务需要与远程进程通信,可以使用Messenger来提供你服务的接口。它允许你在不使用AIDL下实现IPC。

      实现步骤:

      > 服务实现用于接收来自客户端的回调Handler

      > Handler用于创建Messenger对象(其是对Handler的引用)

      > Messenger创建IBinder对象,由service通过onBind()返回给客户端

      > 客户端s使用IBinder实例化Messenger(对服务的Handler的引用),客户端使用它向服务发送Message对象

      >服务在它的Handler中接收每个Message,特别的是在handleMessage()方法中

   在这个方法中,没有客户端s调用服务中的“方法”。而是客户端传递消息(Message对象),服务在它的Handler中接收。

      下面是一个简单的使用Messenger的例子。

View Code
 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     /**
 6      * Handler of incoming messages from clients.
 7      */
 8     class IncomingHandler extends Handler {
 9         @Override
10         public void handleMessage(Message msg) {
11             switch (msg.what) {
12                 case MSG_SAY_HELLO:
13                     Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
14                     break;
15                 default:
16                     super.handleMessage(msg);
17             }
18         }
19     }
20 
21     /**
22      * Target we publish for clients to send messages to IncomingHandler.
23      */
24     final Messenger mMessenger = new Messenger(new IncomingHandler());
25 
26     /**
27      * When binding to the service, we return an interface to our messenger
28      * for sending messages to the service.
29      */
30     @Override
31     public IBinder onBind(Intent intent) {
32         Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
33         return mMessenger.getBinder();
34     }
35 }

  注意到Handler中的 handleMessage()方法是服务接收Mesaage对象,和基于what值决定做什么的地方。

      客户端需要的做的所有事就是创建一个Messenger基于服务返回的IBinder对象,并使用send()发送消息。例如这是一个绑定服务并传送MSG_SAY_HELLO消息到服务的简单活动。

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

  

这个例子不需i暗示服务如何响应客户端,如果你想要这么做,同样需要创建一个在客户端中Messenger对象,然后当客户端接收onServiceConnected()回调的时候,它发送一个Message对象到服务,该消息在send()方法的replyTo参数包含了客户端的Messenger。

       你可以在 MessengerService.java (服务) 和 MessengerServiceActivities.java (客户端) 中看到两种方式如何通信。

绑定一个服务

应用程序组件(客户端)可以通过bindService()绑定到一个服务商。然后Android系统服务的onBind()返回用于与服务交互的IBinder。

      绑定是异步的,bindService()会立即返回,且不会反悔IBinder。为了接收IBinder,客户端必须创建ServiceConnection的实例并通过bindService()传递它。ServiceConnection包含一个回调,系统调用它去传递IBinder对象。

      注:仅仅活动、服务、和内容提供者(activities, services, 和 content providers)可以绑定服务,你不能绑定服务到 broadcast receiver。

      所以,绑定服务的步骤:

      1. 实现ServiceConnection

      你的实现必须重写两个回调:

      onServiceConnected()

            系统调用它传递被onBind()返回IBinder对象

      onServiceDisconnected()

            系统在服务连接异常丢失,如服务崩溃,或者,被杀时调用。当客户端解绑时不被调用。

      2. 调用bindService(),传递ServiceConnection对象

      3. 当系统调用回调函数onServiceConnected()时,可以使用接口定义的方法访问服务

      4. 调用unbindService()断开服务

       当客户端销毁后,他会断开服务,但你总是应该在客户端与服务交互完成或者活动暂停的时候解绑,以使服务在不使用的时候被关闭。

       例如,下面的片段连接客户端到继承Binder的服务,所以所有要做的是将返回的IBinder强转为LocalService类并请求获取LocalService实例。

View Code
 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 
13     // Called when the connection with the service disconnects unexpectedly
14     public void onServiceDisconnected(ComponentName className) {
15         Log.e(TAG, "onServiceDisconnected");
16         mBound = false;
17     }
18 };

  利用传递ServiceConnection参数到bindService(),客户端可以绑定服务。如:

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

 

> bindService()的第一个参数是一个Intent,其指明了要绑定的service的名字(尽管这个Intent可以是implicit的)

      > 第二个参数是ServiceConnection对象

      > 第三个参数是一个标志,来指明绑定的选项,它通常是BIND_AUTO_CREATE,在service不存在时创建。其它的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND或0。

附注

       这个有一些重要的notes:

      >你应该总是捕获DeadObjectException,该异常在连接断开时抛出。这是远程方法抛出的唯一异常

      >Objects are reference counted across processes.

      > 在客户端声生命周期的匹配的建立和卸载时,你应该总是匹配绑定和解绑,比如:

            * 如果只需要在你的活动可见时与服务交互,则应该在onStart()中绑定,并在onStop()中解除绑定。

            * 如果你需要你的活动即使在后台停止的时候也可以接收响应,你可以在onCreate()时绑定service,然后在onDestroy()中解除绑定。要明确,这意味着该活动在整个生命周期都需要用到服务(即使是在后台),所以如果服务在其他的进程中,你增加了这个进程的负担,它变得更可能被系统杀掉。

 

       注:你通常不应该在onResume()和onPause()绑定以及解绑定,因为这些回调函数发生在每次生命周期切换中,你应该确保这些处理发生在最少切换中。同时,如果在你的应用程序中多个活动绑定到同一个服务并且两个活动之间有一个切换,在当前活动解除绑定(在pause过程中),而在下一个活动(在resume过程中)绑定之前,服务或许会被销毁和重新创建。 

管理服务的生命周期(LifeCycle)

当一个服务与所有的客户端s解绑,Android系统会销毁它(除非它也用OnStartCommand()被启动过)。同样的,如果它是仅仅是一个绑定的服务的话,你不必管理你的服务的生命周期,Android系统会基于其是否绑定客户端来管理。

      然而,如果你实现了回调函数OnStartCommand(),你必须明确地停止(stop)这个服务,因为服务被认为是started,此时服务会运行下去,无论其是否被绑定到任何客户端,直到服务自身调用stopSelf()或者别的组件调用stopService()。

      另外,如果服务被启动且接受绑定,那么当系统调用onUnbind()时,如果你想要在下次一个客户端绑定服务时接收onRebind()(而不是onBind())的调用,你可以返回true。onRebind()返回void,但客户端仍在 onServiceConnected()接收IBinder。图1说明此过程。

       更多的started服务的信息,参见Service文档。

 

posted on 2012-04-17 23:15  Dream Splasher  阅读(298)  评论(0)    收藏  举报

导航