绑定Service专题

一、绑定服务

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

绑定服务仍是Service类的实现,可让其他应用绑定,通过实现onBind()回调方法,返回IBinder对象作为客户端与服务进行交互的接口。

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

多个客户端可以同时连接到一个服务。只有在第一个客户端绑定时,系统才会调用服务的onBind()方法来检索IBinder。系统随后无需再次调用onBind(),便可将同一IBinder传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非startService()也启动了该服务)。

当实现绑定服务时,最重要是定义onBind()回调方法返回的接口。可以通过下面介绍的几种方法定义服务的IBinder接口。

二、创建绑定服务

1.扩展Binder类

如果服务仅供本地使用,且与客户端在相同的进程中运行(常见情况),则应优先扩展Binder类。客户端收到Binder之后,利用它直接访问Binder实现中乃至Service中可用的公共方法。

具体步骤:

1).创建一个可以满足下列任一要求的Binder实例:

a.包含客户端可调用的公共方法;

b.返回当前Service实例,其中包含客户端可调用的公共方法;

c.返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法。

2).从onBind()回调方法返回此Binder实例。

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

注意:要求在同一应用内,是为了便于客户端转换返回的对象和正确调用API。要求在同一进程内,则此方法不执行任何跨进程编组。

服务端示例:

public class LocalService extends Service {
    private final IBinder mBinder = new LocalBinder();
    private final Random mGenerator = new Random();

    /**
     * 用于返回给客户端的Binder。因为服务在本地运行,因此不用处理IPC操作
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // 返回LocalService实例,以便方法调用
            return LocalService.this;
        }
    }

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

    /** 供客户端调用的方法 */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}
LocalBinder为客户端提供getService()方法,用以检索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();
        // 绑定到LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 解绑
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    public void onButtonClick(View v) {
        if (mBound) {
            // 从 LocalService 调用方法.
            // 如果调用会阻塞线程,则应在子线程中进行调用
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** 为绑定服务定义回调,传递给bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // 连接成功,将IBinder转换成指定的Binder
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}
上述代码说明了客户端如何使用ServiceConnection的实现和onServiceConnected回调绑定到服务。

2.使用Messenger

如需让接口跨进程工作,则可使用Messenger为服务创建接口。服务可以这种方式定义对应于不同类型Message对象的Handler。此Handler是Messenger的基础,后者随后可与客户端分享一个IBinder,从而让客户端能利用Message对象向服务发送命令。此外,客户端还可以定义自有Messenger,以便服务传回消息。

这是执行IPC最简单的方法,因为Messenger会在单一线程中创建包含所有请求的队列(串行执行请求),这样就不必对服务进行线程安全设计。

具体步骤:

1)服务实现一个Handler,由其接收来自客户端的每个调用的回调;

2)Handler用于创建Messenger对象(对Handler的引用);

3)Messenger创建一个IBinder,通过onBind()使其返回客户端;

4)客户端使用IBinder将Messenger实例化,然后使用后者将Message对象发送给服务;

5)服务在其Handler中(具体讲,是在handleMessage()方法中)接收每个Message。

注意:客户端并没有调用服务的“方法”。而客户端传递的消息(Message对象)是服务在Handler中接收的。

服务端示例:

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;

    /**
     * 接收客户端请求的handler
     */
    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);
            }
        }
    }

    /**
     * 公开给客户端用于发送Message给Handler的Messenger
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * 当绑定到服务时,返回一个接口给messenger用于发送Message给服务
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}
服务在Handler的handleMessage()方法中接收传入的Message,并根据what决定下一步操作。

客户端只需根据服务返回的IBinder创建一个Messenger,然后利用send()发送消息。

客户端示例:

public class ActivityMessenger extends Activity {
    /** 用于与服务交互的Messenger */
    Messenger mService = null;

    boolean mBound;

    /**
     * 与服务接口交互的连接类
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // 当与服务的连接已经建立时调用,给出这个对象,用于与服务交互。
            // 我们将使用Messenger与Service交互,这里从原始的IBinder对象中获取客户端
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // 断开时调用
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // 创建并发送一个Message给服务,使用what传递值
        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();
        // 绑定
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 解除绑定
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

注意,如果想让服务做出响应,则需要在客户端内创建一个Messenger。当客户端收到onServiceConnected()回调时,会向服务发送一条Message,并在其send()方法的replyTo参数中包含客户端的Messenger.

3.使用AIDL

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

如需直接使用AIDL,必须创建一个定义编程接口的.aidl文件。Android SDK利用该文件生成一个实现接口并处理IPC的抽象类,之后便可在服务内对其进行扩展。

注意:大多数应用“都不会”使用AIDL来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL并不适合大多数应用。

具体请见:AIDL详解

三、绑定到服务

客户端通过bindService绑定到服务。Android系统会调用服务的onBind()方法,该方法返回用于与服务交互的IBinder。绑定是异步的。bindService()会立即返回,但不会返回IBinder。要接收IBinder,客户端必须创建一个ServiceConnection实例,并将其传递给bindService()。ServiceConnection包括一个回调方法,系统通过调用它来传递IBinder。

注意:只有Activity、Service与ContentProvider可以绑定到服务,广播不行。

绑定步骤:

1.实现ServiceConnection。

实现回调方法:

onServiceConnected() :系统会调用该方法以传递服务onBind()返回的IBinder。

onServiceDisconnected(): Android 系统会在与服务连接意外中断时(服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统不会调用该方法。

2.调用bindService(),传递ServiceConnection实现。

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

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

注意:如果应用在客户端绑定到服务时销毁客户端,则销毁会导致客户端取消绑定。更好的做法是在客户端与服务交互完成后,立即取消绑定客户端。这样可以关闭空闲服务。

ServiceConnection示例:

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // 与服务连接建立时调用
    public void onServiceConnected(ComponentName className, IBinder service) {
        // 已经绑定到运行在我们自己进程中的明确的服务,所以可以将IBinder转换为具体的类并访问
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // 在意外断开时调用
    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);
intent:要绑定到的服务

Context.BIND_AUTO_CREATE:指示绑定选项的标志。通常是BIND_AUTO_CREATE,以便创建尚未激活的服务。也可能是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或为0。

重点说明:

1.应始终捕获DeadObjectException异常(RemoteException子类),连接中断时引发的。是远程方法引发的唯一异常。

2.对象是跨进程计数的引用。(这句还不太理解,记下来再说)

3. 通常应在生命周期的引入和退出时绑定和取消绑定。一般切勿在onResume()和onPause()绑定和取消绑定。

四、绑定服务的生命周期

当所有客户端都取消绑定时,服务被销毁(除非使用了onStartCommond()启动该服务)。

如果是以onStartCommond()启动服务,则服务会一直运行,直到通过stopSelf()自行停止,或其他组件调用stopService()位置,无论是否绑定到任何客户端。

此外,如果服务启动并接受绑定,当系统调用onUnbind()方法时,如果想在客户端下一次绑定到服务时接收onRebind()调用,则可选则返回true。onRebind()返回空值,但客户端仍在其onServiceConnected()回调中接收IBinder。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值