Android官方文档之Bound Services

Posted vanpersie_9987

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android官方文档之Bound Services相关的知识,希望对你有一定的参考价值。

绑定式Service在CS结构中扮演着Server的角色。绑定式Service允许其他组件(如Activity)绑定该Service、发送请求、接收响应、甚至IPC通信( interprocess communication)。绑定式Service通常服务于其他应用程序的组件、且没有明确的后台的概念(does not run in the background indefinitely)。


本文将介绍bound Service的相关内容,包括其创建、与其他应用组件如何绑定 等。有关Service的基础内容,您可以参考我翻译的官方文档:《Android官方文档之Services》;如需访问bound Service的官方原文,您可以点击这个链接:《Bound Services》


绑定式Service基础(The Basics)


绑定式Service是一个继承于Service的类。它可以与其他应用交互。为了实现绑定Service,您必须重写onBind()方法。该方法返回一个IBinder接口,此接口是绑定式Service与其它应用组件交互的桥梁。


其它应用组件可调用bindService()方法绑定Service。该方法需要传入的参数中包含一个实现了ServiceConnection接口的对象。该对象监控着组件与Service的绑定状态(which monitors the connection with the service)。bindService()方法并不返回数据,而一旦系统创建了组件与Service的连接,ServiceConnection接口中的方法onServiceConnected()将被回调,此时实现了IBinder接口的对象将传递至组件中,这样便实现了Service与绑定组件的通信(to deliver the IBinder that the client can use to communicate with the service)。


Service可同时与多个组件绑定。然而Service仅在绑定的第一个组件时回调onBind()方法以获得IBinder接口对象,之后与该Service绑定的组件都传递的是同一个IBinder接口对象,而且并不再回调onBind()方法。


当Service与绑定它的最后一个组件解绑时,系统将该Service 销毁(destroy),当然若Service还使用start方式启动过(调用startService()方法启动),则该Service并不会destroy。


创建bound Service时,最重要的就是实现onBind()回调方法中的返回接口IBinder,下面将介绍几种不同实现IBinder接口的方式。


创建绑定式Service(Creating a Bound Service)


以下列举了三种实现IBinder接口的方式:

  • 继承Binder类(Extending the Binder class):Binder是一个实现了IBinder接口的类。若Service只允许被本应用所在的进程访问(这是大多数情况),您需要继承Binder类,并将该对象作为onBind()方法的返回值。这样,与Service绑定的组件就可以通过该返回对象访问Binder的继承类中的public方法、甚至是Service中的方法(to directly access public methods available in either the Binder implementation or even the Service)。

    若在您的应用程序中,Service仅作为一个在后台工作的组件,那么这种方式最好不过了。除非您需要Service进行跨进程通信。


  • 使用Messenger(Using a Messenger):如需要使用IBinder进行跨进程通信,您应当为Service创建一个Messenger对象。这样,Service可以定义一个Handler对象以接受不同类型的MessageHandlerMessenger的基础,它可以在客户端与IBinder共享(This Handler is the basis for a Messenger that can then share an IBinder with the client),并允许使用Message对象向Service端发送指令(allowing the client to send commands to the service using Message objects)。除此之外,亦可以在client端定义Messenger,这样Service端可以回传信息。

  • 使用AIDL(Using AIDL):AIDL(android Interface Definition Language)是Android接口定义语言的缩写。大多数应用程序并不应该使用AIDL创建bound Service,因为这需要应用程序有处理多线程的能力,而这会使应用程序变得复杂,在本文中并不打算具体介绍AIDL,如需访问有关AIDL的官方原文,您可以点击这个链接:《Android Interface Definition Language (AIDL)》

继承Binder类(Extending the Binder class)


若您的Service仅是应用程序内部使用,并不需要跨进程通信,那么可以继承Binder类。这样,与Service绑定的组件可以直接访问Service中的public方法。


!请注意:这种继承Binder类的方式仅适用于Service与绑定的组件处于同一应用程序或进程的情况,当然这也是最普遍的情况。举例来说,在播放音乐应用程序中,可以使用这种方式将一个Activity与Service绑定,而Service用于在后台播放音乐。


创建方式:

  1. 在Service类中创建一个继承于Binder的内部类。在Service类中定义public方法,以便client端可以访问。在继承于Binder的内部类中返回该Service实例。将该内部类实例作为onBind()返回参数。
  2. 在客户端中的onServiceConnected()回调方法中接受Binder对象,并访问Service中的public方法。

示例如下:

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实例。这样,client端可以通过该实例访问Service中pubic方法。比如,client端可以访问LocalService中的public方法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;
        }
    };
}

首先,在Activity的onStart()回调方法中调用bindService()绑定LocalService,这时,LocalService中的onCreate()onBind()依次回调;接着,ServiceConnection 中的onServiceConnected()方法回调,表示组件与Service已绑定,这时可以通过回传给onServiceConnected()中的IBinder接口对象获得LocalService实例,一旦获得了该实例,便可以调用LocalService中的public方法,如getRandomNumber()方法。


!请注意:Service应在合适的时候与组件解除绑定,本例中应在onStop()中解除与Service的绑定。


使用Messenger绑定Service(Using a Messenger)


当Service需要进行IPC通信时,应在Service中使用Messenger。使用Messenger的方式如下:

  • 继承Handler类,并实现回调方法handleMessage(),每当client端访问Service中的方法时,handleMessage()都将回调(receives a callback for each call from a client)。

  • 需在Service中创建一个Messenger对象,构造该对象需传入一个Handler参数。

  • 调用MessengergetBinder()返回一个IBinder对象,将该对象作为onBind()回调方法的返回值。

  • client端通过onServiceConnected()回传的IBinder参数,构造Messenger对象,并将Message信息传入Messenger对象,发送给Service。

  • Service在HandlerhandleMessage()方法中接收Message信息。


按照如此方式,client端并没有显式调用Service中的方法,而是传递了Message对象,并在Service的Handler中接收。


以下是Service端示例:

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();
    }
}

以下是client端接收示例:

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;
        }
    }
}

本例中并未包含Service端向client端发送消息的逻辑,如需要Service答复client发送的消息,需在client端也创建一个Messenger对象,当onServiceConnected()方法被回调时,在send()方法中传入replyTo参数。


绑定一个Service(Binding to a Service)


绑定Service是一个异步过程(The binding is asynchronous):应用程序中的组件调用bindService()绑定一个Service,bindService()立即返回;接着系统回调Service的onBind()方法,而client并不会接收到IBinder参数,为了接收该参数,需要创建一个ServiceConnection实例,并将该实例传入bindService()中,系统会将IBinder回传至ServiceConnection的回调方法中(The ServiceConnection includes a callback method that the system calls to deliver the IBinder)。


!请注意:只有activities、services、content providers可以绑定Service, broadcast receiver不能绑定Service(you cannot bind to a service from a broadcast receiver)。


所以,绑定Service应按如下步骤:

  • 实现ServiceConnection接口;

    • 实现onServiceConnected()方法:当client与Service建立绑定时,系统回调该方法,并将onBind()返回的IBinder参数回传至该方法中;

    • 实现onServiceDisconnected()方法:当绑定的Service意外终止时( unexpectedly lost),系统回调该方法,如Service被进程kill或Service崩溃(crashed)。系统若回调unBindService()方法,将不会回调onServiceDisconnected()方法。


  • 调用bindService(),并传入ServiceConnection的实现类对象;

  • 当系统回调onServiceConnected()时,表示client与Service已绑定,此时可以访问Service中的public方法。

  • 系统回调unbindService(),解除绑定。

    • !请注意:当client与Service绑定时,系统destroy掉client端,这将破坏绑定状态(destruction causes the client to unbind)。好的做法是只要client与Service交互完成,就解除绑定(to unbind the client as soon as it is done interacting with the service)。

下面的代码片段演示了如何绑定Service:


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;
    }
};

下面演示了启动绑定的方式:

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

其中第三个参数表示绑定的模式,通常为BIND_AUTO_CREATE,表示当Service还尚未处于alive状态时创建该Service。其它可用的参数为BIND_DEBUG_UNBINDBIND_NOT_FOREGROUND,若不打算指定模式,可传入0。


需要额外注意的地方(Additional notes)


  • 当连接错误时,系统会抛出DeadObjectException异常,这也是在client端调用Service中的方法时可能抛出的唯一异常(This is the only exception thrown by remote methods)。

  • binding 和 unbinding应成对出调用。

    • 若当Activity在前台处于运行状态时,需要与绑定的Service交互,那么应在onStart()方法中bindService(),在onStop()unbindService()

    • 若当Activity在后台处于stop状态时,那么应在onCreate()方法中bindService(),在onDestroy()unbindService()。此时系统将更易kill该Service。


!请注意:请不要在onResume() 和 onPause()方法中绑定、解绑Service,因为这两个生命周期回调方法经常被回调,频繁的绑定与解绑会降低程序的执行效率。


管理Bound Service的生命周期(Managing the Lifecycle of a Bound Service)


当Service不再与任何Client绑定时,系统将回收该Service(除非Service也用Start方式启动了(将回调onStartCommand()方法)),您无需手动管理一个纯bound Service的生命周期(you don’t have to manage the lifecycle of your service if it’s purely a bound service),系统会自动管理。


无论Service绑定了多少个client,若您还回调了onStartCommand()方法,那么必须显式stop该Service,可以通过在Service中调用stopSelf()方法、或在其他组件中调用stopService()stop该Service。


若通过两种方式(start、bound)同时启动了一个Service,那么如果希望Service在下一次绑定该client时回调onRebind()方法,应在onUnbind()方法中返回true。按照这种方式,再次与该Service绑定的client仍可以在onServiceConnected()方法中接收到回传的IBinder 参数。如下图所示:


这里写图片描述

以上是关于Android官方文档之Bound Services的主要内容,如果未能解决你的问题,请参考以下文章

Android官方文档之Services

android基础部分再学习---再谈Service进程服务通信

Android四大组件之Service精通

Android总结 - Service

Android Service的分类详解

android 四大组件值Service 绑定式服务