Android中的绑定服务

Posted by JackPeng on July 22, 2016

绑定服务

绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。 绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。

本文向您介绍如何创建绑定服务,包括如何绑定到来自其他应用组件的服务。 不过,您还应参阅服务文档,了解有关一般服务的更多信息,例如:如何利用服务传送通知、如何将服务设置为在前台运行等等。

基础知识

绑定服务是 Service 类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 onBind() 回调方法。该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。

客户端可通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 方法会立即无值返回,但当 Android 系统创建客户端与服务之间的连接时,会调用 ServiceConnection 上的 onServiceConnected(),向客户端传递用来与服务通信的 IBinder。

多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。

当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。

当您实现绑定服务时,最重要的环节是定义您的 onBind() 回调方法返回的接口。您可以通过几种不同的方法定义服务的 IBinder 接口,下文对这些方法逐一做了阐述。

创建绑定服务

创建提供绑定的服务时,您必须提供 IBinder,用以提供客户端用来与服务进行交互的编程接口。 您可以通过三种方法定义接口:

扩展 Binder 类

如果服务是供您的自有应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。客户端收到 Binder 后,可利用它直接访问 Binder 实现中乃至 Service 中可用的公共方法。 如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。

使用 Messenger

如需让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口。服务可以这种方式定义对应于不同类型 Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。 这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。

使用 AIDL

AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。之前采用 Messenger 的方法实际上是以 AIDL 作为其底层结构。如上所述,Messenger 会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。 如需直接使用 AIDL,您必须创建一个定义编程接口的 .aidl 文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,您随后可在服务内对其进行扩展。

注:大多数应用“都不会”使用 AIDL 来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL 并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 AIDL,请参阅 AIDL 文档。

扩展 Binder 类

如果您的服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 Binder 类,让您的客户端通过该类直接访问服务中的公共方法。

注:此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效(经测试,服务端返回的binder和客户端收到的binder为同一对象,服务和客户端不在同一进程则无法则会发生强转错误)。 例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。

以下是具体的设置方法:

1 在您的服务中,创建一个可满足下列任一要求的 Binder 实例:

  • 包含客户端可调用的公共方法
  • 返回当前 Service 实例,其中包含客户端可调用的公共方法
  • 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
  • 2 从 onBind() 回调方法返回此 Binder 实例。

    3 在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。

    注:之所以要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 API。服务和客户端还必须在同一进程内,因为此方法不执行任何跨进程编组。

    例如,以下这个服务可让客户端通过 Binder 实现访问服务中的方法:

    public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();
    
    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with 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()。

    点击按钮时,以下这个 Activity 会绑定到 LocalService 并调用 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 的实现和 onServiceConnected() 回调绑定到服务。下文更详细介绍了绑定到服务的过程。

    注:上例并未显式取消与服务的绑定,但所有客户端都应在适当的时间(例如当 Activity 暂停时)取消绑定。

    如需查看更多示例代码,请参阅 ApiDemos 中的 LocalService.java 类和 LocalServiceActivities.java 类。

    使用 Messenger

    如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

    以下是 Messenger 的使用方法摘要:

  • 服务实现一个 Handler,由其接收来自客户端的每个调用的回调
  • Handler 用于创建 Messenger 对象(对 Handler 的引用)
  • Messenger 创建一个 IBinder,服务通过 onBind() 使其返回客户端
  • 客户端使用 IBinder 将 Messenger(引用服务的 Handler)实例化,然后使用后者将 Message 对象发送给服务
  • 服务在其 Handler 中(具体地讲,是在 handleMessage() 方法中)接收每个 Message
  • 这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(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();
    }
    }
    

    请注意,服务就是在 Handler 的 handleMessage() 方法中接收传入的 Message,并根据 what 成员决定下一步操作。

    客户端只需根据服务返回的 IBinder 创建一个 Messenger,然后利用 send() 发送一条消息。例如,以下就是一个绑定到服务并向服务传递 MSG_SAY_HELLO 消息的简单 Activity:

    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,并在其 send() 方法的 replyTo 参数中包含客户端的 Messenger。

    如需查看如何提供双向消息传递的示例,请参阅 MessengerService.java(服务)和 MessengerServiceActivities.java(客户端)示例。

    绑定到服务

    应用组件(客户端)可通过调用 bindService() 绑定到服务。Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder。

    绑定是异步的。bindService() 会立即返回,“绝对不会”使 IBinder 返回客户端。要接收 IBinder,客户端必须创建一个 ServiceConnection 实例,并将其传递给 bindService()。ServiceConnection 包括一个回调方法,系统通过调用它来传递 IBinder。

    注:只有 Activity、服务和内容提供程序可以绑定到服务—您无法从广播接收器绑定到服务。

    因此,要想从您的客户端绑定到服务,您必须:

    1 实现 ServiceConnection。

    您的实现必须重写两个回调方法: onServiceConnected() 系统会调用该方法以传递服务的 onBind() 方法返回的 IBinder。

    onServiceDisconnected() Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“绝对不会”调用该方法。 2 调用 bindService() 以传递 ServiceConnection 实现。

    3 当系统调用您的 onServiceConnected() 回调方法时,您可以使用接口定义的方法开始调用服务。

    4 要断开与服务的连接,请调用 unbindService()。

    当您的客户端被销毁时,它将取消与服务的绑定,但您应该始终在完成与服务的交互时或您的 Activity 暂停时取消绑定,以便服务能够在未被占用时关闭。 (下文更详细地阐述了绑定和取消绑定的适当时机。)

    例如,以下代码段通过扩展 Binder 类将客户端与上面创建的服务相连,因此它只需将返回的 IBinder 转换为 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);
    
  • bindService() 的第一个参数是一个 Intent,用于显式命名要绑定的服务(但 Intent 可能是隐式的)
  • 第二个参数是 ServiceConnection 对象
  • 第三个参数是一个指示绑定选项的标志。它通常应该是 BIND_AUTO_CREATE,以便创建尚未激活的服务。 其他可能的值为 BIND_DEBUG_UNBIND 和 BIND_NOT_FOREGROUND,或 0(表示无)。
  • 管理绑定服务的生命周期

    当服务与所有客户端之间的绑定全部取消时,Android 系统便会销毁服务(除非还使用 onStartCommand() 启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理—Android 系统会根据它是否绑定到任何客户端代您管理。

    不过,如果您选择实现 onStartCommand() 回调方法,则您必须显式停止服务,因为系统现在已将服务视为已启动。在此情况下,服务将一直运行到其通过 stopSelf() 自行停止,或其他组件调用 stopService() 为止,无论其是否绑定到任何客户端。

    此外,如果您的服务已启动并接受绑定,则当系统调用您的 onUnbind() 方法时,如果您想在客户端下一次绑定到服务时接收 onRebind() 调用(而不是接收 onBind() 调用),则可选择返回 true。onRebind() 返回空值,但客户端仍在其 onServiceConnected() 回调中接收 IBinder。下文图 1 说明了这种生命周期的逻辑。

    image

    AIDL

    无论学什么东西,最先得弄明白为什么要有这个东西,不要说存在即是合理,存在肯定合理,但是你还是没有明白。对于AIDL有一些人的浅显概念就是,AIDL可以跨进程访问其他应用程序,和其他应用程序通讯,那我告诉你,很多技术都可以访问,如广播(应用A在AndroidManifest.xml中注册指定Action的广播)应用B发送指定Action的广播,A就能收到信息,这样也能看成不同应用之间完成了通讯(但是这种通讯是单向的);还如ContentProvider,通过URI接口暴露数据给其他应用访问;但是这种都算不上是应用之间的通讯。可能最让人迷惑的是Android推出来了Messager,它就是完成应用之间的通讯的。那么为什么还要有AIDL呢,官方文档介绍AIDL中有这么一句话:

    Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

    第一句最重要,“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”,其他情况下你都可以选择其他方法,如使用Messager,也能跨进程通讯。可见AIDL是处理多线程、多客户端并发访问的。而Messager是单线程处理。还是官方文档说的明白,一句话就可以理解为什么要有AIDL。

    在你开始设计你的AIDL接口之前,你要意识到AIDL接口是直接的方法调用。在哪个线程处理完全取决于调用是来自本地还是远程。具体如下:

    *来自本地的调用,执行过程发生在同一个线程里。如果调用发生在UI主线程,则AIDL接口执行也是在主线程。如果调用发生在另一个线程,则AIDL接口的执就在那个线程里。所以如果是本地线程访问service,你完全可以掌控事那个线程在执行(但是这种情况,其实就可以不必使用AIDL了,使用 Binder 获取 service 就行了)。

    *调用来自远程进程。如果同时发生多个请求,你无法得知是哪个线程(服务进程中有一个线程池,专门用来处理并发请求的),也就是说AIDL接口必须确保线程安全。

    *调用来自远程且实用 oneway关键字,远程调用将不会阻塞,发送transaction data数据之后方法立即返回,通常用于远程服务端的回调,测试发现,该回调不直接回调aidl接口的方法,而是会缓冲到一个队列(线程池?)后立即返回,然后有binder线程池依次通知客户端。如果本地进程调用,oneway依然是同步执行。

    实用AIDL创建bounded service的步骤:

    1 Create the .aidl file

    创建aidl文件,在AS中,右键module点击new-aidl,如下图:

    image

    // IRemoteService.aidl
    package com.example.android;
    
    // Declare any non-default types here with import statements
    
    /** Example service interface */
    interface IRemoteService{
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();
    
    /** Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt,long aLong,boolean aBoolean,float aFloat,
            double aDouble,String aString);
    }
    

    在AS中,默认不会自动生成对应的java文件,需要点击工具栏的make按钮,会在/Application/build/generated/source/aidl/debug/com/example/android/displayingbitmaps/IMyAidlInterface.java。

    该文件有一个stub静态内部类,如下:

    public static abstract class Stub extends android.os.Binder implements com.example.android.displayingbitmaps.IMyAidlInterface
    

    2 Implement the interface

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub(){
    public int getPid(){
        returnProcess.myPid();
    }
    public void basicTypes(int anInt,long aLong,boolean aBoolean,
        float aFloat,double aDouble,String aString){
        // Does nothing
    }
    };
    

    3 Expose the interface to clients

    Service端:

    public class RemoteService extends Service{    @Override
    public void onCreate(){
        super.onCreate();
    }
    
    @Override
    public IBinder onBind(Intent intent){
        // Return the interface
        return mBinder;
    }
    
    private finalI RemoteService.Stub mBinder = newIRemoteService.Stub(){
        public int getPid(){
            returnProcess.myPid();
        }
        public void basicTypes(int anInt,long aLong,boolean aBoolean,
            float aFloat,double aDouble,String aString){
            // Does nothing
        }
    };
    }
    

    Client端:

    IRemoteService mIRemoteService;
    private ServiceConnection mConnection = new ServiceConnection(){
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className,IBinder service){
        // Following the example above for an AIDL interface,
        // this gets an instance of the IRemoteInterface, which we can use to call on the service
        mIRemoteService = IRemoteService.Stub.asInterface(service);
    }
    
    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className){
        Log.e(TAG,"Service has unexpectedly disconnected");
        mIRemoteService =null;
    }
    };
    

    通过IPC传递对象

  • 某些时候传递的对象不是Android默认支持的那些,我们需要自己使得这个对象Parcelable化,实现步骤如下:
  • Make your class implement the Parcelable interface.

    Implement writeToParcel, which takes the current state of the object and writes it to a Parcel.

    Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface.

    Finally, create an .aidl file that declares your parcelable class (as shown for the Rect.aidl file, below).If you are using a custom build process, do not add the .aidl file to your build. Similar to a header file in the C language, this .aidl file isn’t compiled.

  • 首先需要在aidl文件中声明这个对象类型
  • package android.graphics;
    
    // Declare Rect so AIDL can find it and knows that it implements
    // the parcelable protocol.
    parcelable Rect;
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public final class Rect implements Parcelable{
    public int left;
    public int top;
    public int right;
    public int bottom;
    
    public static final Parcelable.Creator<Rect> CREATOR = new
    Parcelable.Creator<Rect>(){
        public Rect createFromParcel(Parcelin){
            return new Rect(in);
        }
    
        public Rect[] newArray(int size){
            return new Rect[size];
        }
    };
    
    public Rect(){
    }
    
    private Rect(Parcelin){
        readFromParcel(in);
    }
    
    public void writeToParcel(Parcelout){
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }
    
    public void readFromParcel(Parcelin){
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
    }
    

    调用IPC的方法

    Include the .aidl file in the project src/ directory.

    Declare an instance of the IBinder interface (generated based on the AIDL).

    Implement ServiceConnection.

    Call Context.bindService(), passing in your ServiceConnection implementation.

    In your implementation of onServiceConnected(), you will receive an IBinder instance (called service). Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter toYourInterface type.

    Call the methods that you defined on your interface. You should always trap DeadObjectException exceptions, which are thrown when the connection has broken; this will be the only exception thrown by remote methods.

    To disconnect, call Context.unbindService() with the instance of your interface.

    public static class Binding extends Activity {
    /** The primary interface we will be calling on the service. */
    IRemoteService mService = null;
    /** Another interface we use on the service. */
    ISecondary mSecondaryService = null;
    
    Button mKillButton;
    TextView mCallbackText;
    
    private boolean mIsBound;
    
    /**
     * Standard initialization of this activity.  Set up the UI, then wait
     * for the user to poke it before doing anything.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.remote_service_binding);
    
        // Watch for button clicks.
        Button button = (Button)findViewById(R.id.bind);
        button.setOnClickListener(mBindListener);
        button = (Button)findViewById(R.id.unbind);
        button.setOnClickListener(mUnbindListener);
        mKillButton = (Button)findViewById(R.id.kill);
        mKillButton.setOnClickListener(mKillListener);
        mKillButton.setEnabled(false);
    
        mCallbackText = (TextView)findViewById(R.id.callback);
        mCallbackText.setText("Not attached.");
    }
    
    /**
     * 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 service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            mService = IRemoteService.Stub.asInterface(service);
            mKillButton.setEnabled(true);
            mCallbackText.setText("Attached.");
    
            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                mService.registerCallback(mCallback);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even
                // do anything with it; we can count on soon being
                // disconnected (and then reconnected if it can be restarted)
                // so there is no need to do anything here.
            }
    
            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_connected,
                    Toast.LENGTH_SHORT).show();
        }
    
        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;
            mKillButton.setEnabled(false);
            mCallbackText.setText("Disconnected.");
    
            // As part of the sample, tell the user what happened.
            Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };
    
    /**
     * Class for interacting with the secondary interface of the service.
     */
    private ServiceConnection mSecondaryConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // Connecting to a secondary interface is the same as any
            // other interface.
            mSecondaryService = ISecondary.Stub.asInterface(service);
            mKillButton.setEnabled(true);
        }
    
        public void onServiceDisconnected(ComponentName className) {
            mSecondaryService = null;
            mKillButton.setEnabled(false);
        }
    };
    
    private OnClickListener mBindListener = new OnClickListener() {
        public void onClick(View v) {
            // Establish a couple connections with the service, binding
            // by interface names.  This allows other applications to be
            // installed that replace the remote service by implementing
            // the same interface.
            bindService(new Intent(IRemoteService.class.getName()),
                    mConnection, Context.BIND_AUTO_CREATE);
            bindService(new Intent(ISecondary.class.getName()),
                    mSecondaryConnection, Context.BIND_AUTO_CREATE);
            mIsBound = true;
            mCallbackText.setText("Binding.");
        }
    };
    
    private OnClickListener mUnbindListener = new OnClickListener() {
        public void onClick(View v) {
            if (mIsBound) {
                // If we have received the service, and hence registered with
                // it, then now is the time to unregister.
                if (mService != null) {
                    try {
                        mService.unregisterCallback(mCallback);
                    } catch (RemoteException e) {
                        // There is nothing special we need to do if the service
                        // has crashed.
                    }
                }
    
                // Detach our existing connection.
                unbindService(mConnection);
                unbindService(mSecondaryConnection);
                mKillButton.setEnabled(false);
                mIsBound = false;
                mCallbackText.setText("Unbinding.");
            }
        }
    };
    
    private OnClickListener mKillListener = new OnClickListener() {
        public void onClick(View v) {
            // To kill the process hosting our service, we need to know its
            // PID.  Conveniently our service has a call that will return
            // to us that information.
            if (mSecondaryService != null) {
                try {
                    int pid = mSecondaryService.getPid();
                    // Note that, though this API allows us to request to
                    // kill any process based on its PID, the kernel will
                    // still impose standard restrictions on which PIDs you
                    // are actually able to kill.  Typically this means only
                    // the process running your application and any additional
                    // processes created by that app as shown here; packages
                    // sharing a common UID will also be able to kill each
                    // other's processes.
                    Process.killProcess(pid);
                    mCallbackText.setText("Killed service process.");
                } catch (RemoteException ex) {
                    // Recover gracefully from the process hosting the
                    // server dying.
                    // Just for purposes of the sample, put up a notification.
                    Toast.makeText(Binding.this,
                            R.string.remote_call_failed,
                            Toast.LENGTH_SHORT).show();
                }
            }
        }
    };
    
    // ----------------------------------------------------------------------
    // Code showing how to deal with callbacks.
    // ----------------------------------------------------------------------
    
    /**
     * This implementation is used to receive callbacks from the remote
     * service.
     */
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
        /**
         * This is called by the remote service regularly to tell us about
         * new values.  Note that IPC calls are dispatched through a thread
         * pool running in each process, so the code executing here will
         * NOT be running in our main thread like most other things -- so,
         * to update the UI, we need to use a Handler to hop over there.
         */
        public void valueChanged(int value) {
            mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
        }
    };
    
    private static final int BUMP_MSG = 1;
    
    private Handler mHandler = new Handler() {
        @Override public void handleMessage(Message msg) {
            switch (msg.what) {
                case BUMP_MSG:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    
    };
    }