现在的位置: 首页 > 综合 > 正文

android – 绑定服务(bound services)

2012年03月28日 ⁄ 综合 ⁄ 共 9994字 ⁄ 字号 评论关闭

Bound Services(绑定服务 

 

绑定服务是有一个接口可以在客户端和服务端之间帮助通信服务。绑定服务允许组件(比如activities)绑定它,组件可以发送请求,收到响应,并且可以与service在进程之间通讯。一个绑定服务通常的生命周期与它服务于的其他应用程序组件一同存在,不会在后台一直运行下去。

这个文档告诉你如何去创建一个绑定服务,包括如何绑定服务。

The Basics


绑定服务是允许其他的组件绑定它,并与它进行交互。让一个service有绑定功能,就必须实现onBind()方法,这个方法返回IBinder 对象,这个IBinder对象定义了客户端如何与服务端通信。

客户端可以调用bindService()方法与service进行绑定。当已经绑定,它必须提供ServiceConnection的实现,可以监控与service的连接。 bindService()方法会立刻返回,并且没有值,但是,当系统在客户端和服务端创建链接时,ServiceConnection上的onServiceConnected()会调,得到IBinder对象。

对个客户端可以同时连接一个service。然而,系统第一次调用onBind()的时候才会返回IBinder对象,系统将这个IBinder对象返回给绑定它的客户,不会重复调用onBind().

当没有客户绑定它时,系统就会销毁服务.

在实现绑定服务的时候,定义IBinder接口是非常重要的。定义这个接口有多种方式,下面将会讨论。

Creating a Bound Service


创建绑定服务,必须提供IBinder接口,有三种方式定义这个接口:

Extending the Binder class
如果这个service只是为当前应用服务,并且运行在相同的进程里,创建的接口必须继承Binder类,并且在onBind()方法中返回。客户可以通过它得到Binder对象,通过这个对象可以直接访问共有函数。

当service仅仅为自己的应用服务,这是首选技术。 

Using a Messenger
如果需要接口在不同的进程间使用,可以用Messenger 为service类创建接口。用这种方式, service对处理不同的Message 对象定义了一个Handler. 这个Handler是Messenger的基础,可以与客户分享IBinder,允许客户用message发送命令到service上。补充点,客户端可以自定义Messenger,因此service可以发送消息回来。

这是在进程间通信最简单的方式。因为Messenger队列所有的请求发生在一个线程上,因此不需要考虑service的线程安全。

Using AIDL
AIDL (Android Interface Definition Language) 可以把对象分解成系统能够识别的单元,已达到在进程间通信的能力。在前面的技术中,使用的messenger,实际上是以AIDL作为基础的结构。如上所述,Messenger在一个线程中创建了所有客户请求的一个队列。如果你想service能偶同时处理多个请求,那么就可以直接用AIDL. 在这种案例中,service 必须要有处理多线程的能力,并且是线程安全的。

要想直接使用AIDL,必须创建定义程序的接口文件,后缀是.aidl。Android SDK工具可以通过这个文件生成一个实现了这个接口和处理IPC的抽象类,自定义的service需要继承自这个抽象类。

Note: 大部分的应用不需要使用AIDL创建绑定service,因为它可能需要多线程功能和实现上更加复杂。就AIDL而论,它不适合大部分的应用,并且这个份文档不会讨论AIDL的使用。如果需要直接使用AIDL,可以查阅AIDL文档。

Extending the Binder class

如果service仅仅是用在本应用,并且不需要在进程间通讯,这样实现用户端可以直接访问的Binder类,它可以帮助客户端访问service里面的共有方法。

Note: 这个service必须是在同一个应用和进程中才能工作,这种方式也是最常见的。例如,在音乐应用中,activity需要和他它自己service绑定,以便能在后台播放音乐。

以下是设置的方法:

  1. 在service中,创建一个Binder的实例,这个Bind必须要满足:

    • 包括客户端可以调用的共有方法
    • 返回当前service实例
    • 或者,返回另一个类的一个实例
  2. onBind()方法要返回Binder对象
  3. 在客户端,从onServiceConnected()获得Binder对象,调用提供的方法操作绑定服务。

Note: service与client必须在同一个应用的原因是,client可以将返回对象强制转换成合理的对象,并且调用它的API.service和client必须在同一个进程中,这种方式不能处理进程间的信号处理。

例如,这里有service,client可以通过Binder调用它里面的方法:

publicclassLocalServiceextendsService{
    // Binder given to clients
    privatefinalIBinder mBinder =newLocalBinder();
    // Random number generator
    privatefinalRandom mGenerator =newRandom();

    /**
     * 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.
     */
    publicclassLocalBinderextendsBinder{
        LocalService getService(){
            // Return this instance of LocalService so clients can call public methods
            returnLocalService.this;
        }
    }

    @Override
    publicIBinder onBind(Intent intent){
        return mBinder;
    }

    /** method for clients */
    publicint getRandomNumber(){
      return mGenerator.nextInt(100);
    }}

client 通过LocalBinder 的共有方法(getService())可以得到LocalService。client通过这个service可以调用它的共有方法。例如,clients可以调用getRandomNumber().

下面有个activity,它与LocalService 绑定了,当双击button的时候调用了getRandomNumber() :

publicclassBindingActivityextendsActivity{
    LocalService mService;
    boolean mBound =false;

    @Override
    protectedvoid onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protectedvoid onStart(){
        super.onStart();
        // Bind to LocalService
        Intent intent =newIntent(this,LocalService.class);
        bindService(intent, mConnection,Context.BIND_AUTO_CREATE);
    }

    @Override
    protectedvoid 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) */
    publicvoid 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() */
    privateServiceConnection mConnection =newServiceConnection(){

        @Override
        publicvoid 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
        publicvoid onServiceDisconnected(ComponentName arg0){
            mBound =false;
        }
    };}

上面的实例展示client如何实现ServiceConnection和实现ServiceConnection的回调方法onServiceConnected() 与service绑定.

Note: 上面的例子并没有显示的取消和service的绑定,所有的clients必须在合适的时候解除绑定(activity停止)。

想了解更加详细的例子代码,可以在APiDemo里面查找LocalService.java 和 LocalServiceActivities.java

Using a Messenger

如果service需要与远程的进程通讯,可以用Messenger为service提供一个接口。这种技术可以处理进程间的通讯。

下面是使用messenger的简介:

  • service 要实现Handler,它可以收到每一个client调用的回调。
  • Handler用于创建Messenger对象。 (which is a reference to the Handler).
  • Messenger创建一个IBinder,client调用onBind()时,IBinder要返回给client的
  • Clients 通过IBinder实例化Messenger(that references the service's Handler),Messenger为了client发送Message对象到service。
  • service 通过Handler收到每一个Message, 在 handleMessage() 方法明确的处理.

利用这种方式,service中没有方法可一个被Client调用。client通过传递消息(messages)到service的Handler中。

下面是一个简单的例子,通过Messenger的方式实现:

publicclassMessengerServiceextendsService{
    /** Command to the service to display a message */
    staticfinalint MSG_SAY_HELLO =1;

    /**
     * Handler of incoming messages from clients.
     */
    classIncomingHandlerextendsHandler{
        @Override
        publicvoid 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.
     */
    finalMessenger mMessenger =newMessenger(newIncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    publicIBinder onBind(Intent intent){
        Toast.makeText(getApplicationContext(),"binding",Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }}

主意:Handler 的handleMessage()方法收到Message,并且根据Message的what变量 确定需要做什么。

Client的需要实现的是,基于返回的IBinder创建Messenger对象,并且通过send()发送Message给service的Handler。例如,下面的activity绑定了service,而且传递MSG_SAY_HELLO message到service中处理:

publicclassActivityMessengerextendsActivity{
    /** 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.
     */
    privateServiceConnection mConnection =newServiceConnection(){
        publicvoid 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 =newMessenger(service);
            mBound =true;
        }

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

    publicvoid 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
    protectedvoid onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protectedvoid onStart(){
        super.onStart();
        // Bind to the service
        bindService(newIntent(this,MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protectedvoid onStop(){
        super.onStop();
        // Unbind from the service
        if(mBound){
            unbindService(mConnection);
            mBound =false;
        }
    }}

注意: 这个例子没有体现service是如何响应client。如果想service响应client,需要在client里面创建一个Messenger。当client收到onServiceConnected()回调,就会发送Message到service,send()方法的Message变量replyTo包括客户端的Messenger.

可以从MessengerService.java (service) 和MessengerServiceActivities.java (client) 例子中知道如何提供双向通讯.

Binding to a Service


应用的组件调用bindService()可以绑定service。系统会调用service的onBind()方法,返回一个IBinder,以便组件能与service很好的交互。

绑定过程是异步。bindService()能很快的返回,并且不会返回IBinder到客户端。client收到IBinder时,会创建ServiceConnection 并且通过它绑定到service。ServiceConnection 包括一个系统调用传递IBinder的方法。

Note: 仅仅activities, services, 和content providers 能够与service绑定-不能把broadcast receiver与service绑定。

因此,为了让client与service绑定,必须:

  1. 实现 

抱歉!评论已关闭.