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 参数。如下图所示:



时间: 2024-10-01 03:12:52

Android官方文档之Bound Services的相关文章

Android官方文档之App Components(Activities)

Activity是Android四大组件之首,本文将介绍Activity的含义.创建.启动.销毁.生命周期 等. 如需访问官方原文,您可以点击这个链接:<Activities> Activities Activity是一个类,它是Android呈现界面的载体,用于与用户操作交互,如拨号.照相.发送邮件.展示地图 等.每个Activity都承载了一个Window,这个Window用来绘制UI(User Interface).一般情况下,该Window铺满(fill)整个屏幕:有时候,它也可以悬浮

Android官方文档系列(翻译)

下面一些内容是我翻译Android官方文档相关文章. 菜鸟上路,一边学习一边整理,若您发现有错误的地方还请不吝赐教. Activity 解读Android之Activity基础知识 解读Android之任务和Back栈 Service 解读Android之Service(1)基础知识 解读Android之Service(2)Bound Service 解读Android之Service(3)AIDL ContentProvider 解读Android之ContentProvider(1)CRUD

【android官方文档】与其他App交互

发送用户到另外一个App YOU SHOULD ALSO READ 内容分享 One of Android's most important features is an app's ability to send the user to another app based on an "action" it would like to perform. For example, if your app has the address of a business that you'd

Android官方文档之App Components(Common Intents)

Intent的真正强大之处在于它的隐式Intent,隐式Intent需要配合Intent-filters使用,有关这方面的概念和知识,您可以参考我翻译的官方文档:<Android官方文档之App Components(Intents and Intent Filters)>. 隐式Intent足够强大,以至于系统提供了大量的Intent方便开发者启动系统应用程序,本文将介绍一些常用的隐式Intent.以及如何自定义intent-filters以匹配隐式intent. 如需阅读官方原文,您可以点

Android官方文档之App Resources(中)

本文将继续介绍App Resources中的资源类型(Animation.Color State List.String.Style). 如果需要了解Android中的资源规范,您可以访问我翻译的这篇官方文档:<Android官方文档之App Resources(上)>. 如需访问官方原文,您可以点击这个链接:<Resource Types>. 在下一篇文章中(Android官方文档之App Resources(下)),将介绍App Resources中其余的资源类型(Layout

Android 官方文档:(二)应用清单 —— 2.26 &amp;lt;uses-permission&amp;gt;标签

syntax: <uses-permission android:name="string"         android:maxSdkVersion="integer" /> contained in: <manifest> description: Requests a permission that the application must be granted inorder for it to operate correctly.

Android 官方文档:(一)动画和图像 —— 1.5 画布和画图

The Android framework APIs provides a set 2D drawing APIs that allow you to render your owncustom graphics onto a canvas or to modify existing Views to customize their look and feel.When drawing 2D graphics, you'll typically do so in one of two ways:

Android官方文档之App Resources(下)

本文将介绍Android中Resource Types的drawable.menu.layout.如需访问官方原文,您可以点击这些链接: <Drawable Resources> <Layout Resource> <Menu Resource> 如需了解Android中的其他资源介绍(Animation.Color State List.String.Style),您可以点击这个链接:<Android官方文档之App Resources(中)>. 布局资源

【Android官方文档】翻译Android官方文档-Activities(一)

Activity是可以给用户提供交互操作的程序组件,例如打电话,拍照,发送邮件,抑或者是显示地图.通常窗口会填满屏幕,但是也可以做到比屏幕小或者是悬浮在窗口顶部. App通常由多个Activities组成,它们之间支持相互跳转.一般情况下,每个Activity在应用中都是特别的,就好像 主Activity一样,主activity是应用第一个Activity,其他Activity可以通过其他操作启动.一个新的Activity的启动,那么旧Activity就会被停止,但是系统会保存这些activit