Bound services

一、基础知识

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

二、创建绑定服务

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

  • 扩展Binder
    如果服务是提供给自己应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展Binder类并从onBind()返回该类的实例来创建接口。收到Binder后,客户端可利用其直接访问Binder实现或Service中可用的公共方法。
  • 使用Messenger
    如果让接口跨不同进程工作,可以使用Messenger为服务创建接口。服务可借此方法定义Handler,以响应不同类型的Message对象。此HandlerMessenger的基础,后者随后可与客户端分享一个IBinder,以便客户端能利用Message对象向服务发送命令。此外,客户端还可定义自有Massenger,以便服务回传消息。
    这是执行进程间通信最简单的方法,因为Massenger会在单个线程中创建包含所有请求的队列,这样就不必对服务进行线程安全设计。
  • 使用AIDL
    对于Messenger的方法而言,其实际上是以AIDL作为其底层结构。如上所述,Messenger会在单个线程中创建包含所有客户端请求的队列,以便Service一次接收一个请求。不过,如果想让服务同时处理多个请求,则可直接使用AIDL。在此情况下,Service必须达到线程安全的要求,并且能够进行多线程处理。
    如果直接使用AIDL,需要创建定义编程接口的.aidl文件。Android SDK工具会利用该文件生成实现接口和处理IPC的抽象类,随后可在服务内对该类进行扩展。

    大多数应用不应该使用AIDL来创建绑定服务,因为它可能需要多线程处理能力,并可能导致更为复杂的实现。因此,AIDL并不适合大多数应用。

2.1 扩展Binder

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

注意:只有客户端和服务处于同一应用和进程内(最常见的情况)时,此方法才有效。例如,此方法非常适用于将Activity绑定到某个音乐应用的自有服务,进而实现在后台播放音乐。

以下是具体的设置方法:

  1. Service中,创建可执行以下某种操作的Binder实例:
    • 包含客户端可调用的公共方法。
    • 返回当前的Service实例,该实例中包含客户端可调用的公共方法。
    • 返回有服务承载的其他类的实例,其中包含客户端可调用的公共方法。
  2. onBind()回调方法返回此Binder实例。
  3. 在客户端中,从onServiceConnected()回调方法接收Binder,并使用提供的方法调用绑定服务。

注意:服务和客户端必须在同一个应用内,这样客户端才能转换返回的对象并正确调用其API。服务和客户端还必须在同一进程中,因此此方法不执行任何跨进程组。

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

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder binder = 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 binder;
    }

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

上例展示客户端如何使用ServiceConnection的实现和onServiceConnected()回调绑定到服务。

注意:在上例中,onStop()方法取消了客户端与服务的绑定。

2.2 使用Messenger

如需让Service与远程进程通信,则可使用MessengerService提供接口。借助此方法,无需使用AIDL便可执行进程间通信。

为接口使用Messenger比使用AIDL更简单,因为Messenger会将所有Service调用加入队列。纯AIDL接口会同时向Service发送多个请求,Service随后必须执行多线程处理。

对于大多数应用,Service无需执行多线程处理,因此使用Messenger即可让Service一次处理一个调用。如果服务必须执行多线程处理,则应该使用AIDL来定义接口
以下是Messenger的使用方法摘要:

  1. 服务实现一个Handler,由该类为每个客户端调用接收回调。
  2. 服务使用Handler来创建Messenger对象(对Handler的引用)。
  3. Messenger创建一个IBinder,服务通过onBind()使其返回客户端。
  4. 客户端使用IBinderMessenger(其引用服务的Handler)实例化,然后使用后者将message对象发送给服务。
  5. 服务在其Handler中(具体是在handleMessage()方法中)接收每个message

以下示例展示如何使用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.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * 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();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

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

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

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

    /**
     * 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);
            bound = 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;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) 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 (bound) {
            unbindService(mConnection);
            bound = false;
        }
    }
}

此示例并未说明服务说明如何对客户端做出响应。如果想让服务做出响应,还需在客户端中创建一个Messenger。收到onServiceConnected()回调时,客户端会向服务发送Message,并在其send()方法的replyTo参数中加入客户端的Messenger

三、绑定到服务

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

绑定为异步操作,并且bindService()无需将IBinder返回至客户端即立即返回。如要接收IBinder,客户端必须创建一个ServiceConnection实例,并将其传递给bindService()ServiceConnection包含一个回调方法,系统通过调用该方法来传递IBinder

注意:只有ActivityServiceContentProvider可以绑定到服务,无法从广播接收器绑定到服务。

如要从客户端绑定到服务,应该执行以下步骤:

  1. 实现ServiceConnection。必须重写两个回调方法:onServiceConnected()系统会调用该方法,进而传递服务的onBind()方法所返回的IBinder
    onServiceDisconnected()当与服务的连接意外中断(例如服务崩溃或被终止)时,系统会调用该方法。当客户端取消绑定时,系统不会调用该方法
  2. 调用bindService(),从而传递ServiceConnection实现。如果该方法返回“false”,则说明客户端未与服务进行有效连接,但是,客户端仍应调用unbindService()
  3. 当系统调用onServiceConnected()回调方法时,可以使用接口定义的方法开始调用服务。
  4. 如果断开与服务的连接,应该调用unbindService()

说明
在匹配客户端生命周期的引入(bring-up)和退出(tear-down)时,需要配对绑定和取消绑定:

  • 如果要在Activity可见时与Service交互,则应在onStart()期间进行绑定,在onStop()期间取消绑定。
  • Activity在后台处于停止运行状态时,若仍希望其能够接受响应,则可在onCreate()期间进行绑定,在onDestroy()期间取消绑定。

通常情况下,不应该在ActivityonResume()onPause()期间绑定和取消绑定,因为每次切换生命周期状态时都会发生这些回调,并且应让这些转换期间的处理工作保持最少。

四、管理绑定服务的生命周期

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

如果,选择实现onStartCommand()回调方法,则必须显示停止服务,因为系统现已将其视为已启动状态。在此情况下,服务将一直运行,直到其通过stopSelf()自行停止,或其他组件调用stopService()

此外,如果服务已启动并接受绑定,则当系统调用onUnbind()方法时,如果想在客户端下一次绑定到服务时接收onRebind()调用,则可返回trueonRebind()返回空值,客户端仍在其onServiceCOnnected()回调中接收IBinder。下图说明这些生命周期的逻辑。
已启动且允许绑定的服务的生命周期

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值