Android开发——Service的学习(下)
Bound Service 是客户端-服务器模式的服务,它允许允许组件(比如activity)对其进行绑定、发送请求、接收响应、甚至进行进程间通信(IPC)。为了提供绑定,开发人员必须实现onBind()毁掉方法,该方法返回IBinder对象,它定义了客户端用来与服务交互的程序接口。
客户端通过bindService()方法绑定到服务。此时,客户端必须提供ServiceConnection接口的实现类,它监视客户端和服务之间的连接。多个客户端能够同时连接服务器。然而仅当第一个客户端绑定时,系统调用服务的onBind()方法来获取IBinder对象。然后,系统会向后续请求绑定的客户端传送这同一个 IBinder ,而不再调用 onBind() 。
当最后一个客户端解除绑定后,系统会销毁服务(除非服务同时是通过 startService() 启动的)。
一:创建Bound服务
当你实现自己的bound服务时,最重要的工作就是定义 onBind() 回调方法所返回的接口。 有一下三种方式:
(1)继承Binder类
如果服务是你的应用程序所私有的,并且与客户端运行于同一个进程中(通常都是如此),你应该通过j继承 Binder 类来创建你的接口,并从 onBind() 返回一个它的实例。客户端接收该 Binder 对象并用它来直接访问 Binder 实现类或者 Service 中可用的公共(public)方法。
如果服务只应用于私有应用程序,那这就是首选的技术方案。如果服务要被其它应用程序使用或者访问独立进程时,则不能使用该技术。
仅当客户端与服务位于同一个应用程序和进程时,才可以。例如音乐播放我绑定activity到自己的服务来在后台播放音乐。
实现步骤如下:
- 在你的服务中,创建一个 Binder 的实例,实现以下三者之一:
(1)包含客户端能调用的公共方法
(2)返回当前service实例,其中包含客户端能够调用的公共方法
(3)返回服务管理的其他类的实例,其中包含客户端能够调用的公共方法
- 从回调方法 onBind() 中返回 Binder 类的实例。
- 在客户端中,在回调方法 onServiceConnected() 中接收 Binder 类实例并用所提供的方法对绑定的服务进行调用。
以下是一个服务的示例,它通过实现一个 Binder 来为客户端访问它内部的方法提供支持:
public class LocalService extends Service { // 给客户端的Binder private final IBinder mBinder = new LocalBinder(); // 产生随机数 private final Random mGenerator = new Random(); 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()。
以下是一个绑定到LocalService的activity,当点击按钮时,它会调用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; } } /** 点击Button时,调用此方法 */ public void onButtonClick(View v) { if (mBound) { // 调用LocalService中的方法。 // 不过,如果该调用会导致某些操作的挂起,那么调用应该放入单独的线程中进行, // 以免降低activity的性能。 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转型获得LocalService实例 LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }
上述例子展示了客户端如何利用 ServiceConnection 和 onServiceConnected() 回调方法绑定到服务。
(2)使用Messenger类
如果你的服务需要与远程进程进行通信,那你可以使用一个 Messenger 来提供服务的接口。这种技术能让你无需使用AIDL就能进行进程间通信(IPC),这是执行进程间通信(IPC)最为简便的方式。
以下是使用方法:
- 服务实现一个 Handler ,用于客户端每次调用时接收回调。
- 此 Handler 用于创建一个 Messenger 对象(它是一个对 Handler 的引用)。
- 此 Messenger 对象创建一个 IBinder ,服务在 onBind() 中把它返回给客户端。
- 客户端用 IBinder 将 Messenger (引用服务的 Handler )实例化,客户端用它向服务发送消息对象 Message 。
- 服务接收 Handler 中的每个消息 Message ——确切的说,是在 handleMessage() 方法中接收。
通过这种方式,客户端不需要调用服务中的“方法”。取而代之的是,客户端发送“消息”( 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); } } } /** * 给客户端使用的用来发送消息给 IncomingHandler.即为,第二步。 */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * 返回一个接口用来给服务发送消息,即为,第三步 */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } }
客户端要做的全部工作就是根据服务返回的 IBinder 创建一个 Messenger ,并用 send() 方法发送一个消息。例如,以下是一个activity示例,它绑定到上述服务,并向服务发送 MSG_SAY_HELLO消息:
public class ActivityMessenger extends Activity { /** 和service沟通的Messenger. */ Messenger mService = null; /** 是否已经绑定 */ boolean mBound; /** * 与服务进行交互 */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // 与服务建立联接后将会调用本方法, // 给出用于和服务交互的对象。 // 我们将用一个Messenger来与服务进行通信, // 因此这里我们获取到一个原始IBinder对象的客户端实例。 mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // 当与服务的联接被意外中断时——也就是说服务的进程崩溃了, // 将会调用本方法。 mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // 创建并向服务发送一个消息,用到了已约定的'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; } } }
二:绑定到服务
应用程序组件(客户端)可以通过调用 bindService() 来绑定服务。然后Android系统会调用服务的 onBind() 方法,返回一个用于和服务进行交互的 IBinder 。
绑定是异步进行的。 bindService() 将立即返回,并不会向客户端返回 IBinder 。为了接收 IBinder ,客户端必须创建一个 ServiceConnection 的实例,并把它传给 bindService() 。 ServiceConnection 包含了一个回调方法,系统将会调用该方法来传递客户端所需的那个 IBinder 。
注意: 只有activity、service和content provider才可以绑定到服务上——broadcast receiver不能绑定服务。
因此,要把客户端绑定到服务上,你必须:
- 实现 ServiceConnection 。你的实现代码必须重写两个回调方法:onServiceConnected()系统调用该方法来传递服务的 onBind() 方法所返回的 IBinder 。onServiceDisconnected()当与服务的联接发生意外中断时,比如服务崩溃或者被杀死时,Android系统将会调用该方法。客户端解除绑定时,不会调用该方法。
- 调用 bindService() ,传入已实现的 ServiceConnection 。
- 当系统调用你的 onServiceConnected() 回调方法时,你可以利用接口中定义的方法开始对服务的调用。
- 要断开与服务的联接,请调用 unbindService() 。
当客户端被销毁时,与服务的绑定也将解除。但与服务交互完毕后,或者你的activity进入pause状态时,你都应该确保解除绑定,以便服务能够在用完后及时关闭。
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);