绑定服务是 Service
类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 onBind()
回调方法。该方法返回的IBinder
对象定义了客户端用来与服务进行交互的编程接口。
bindService()
绑定到服务。调用时,它必须提供
ServiceConnection
的实现,后者会监控与服务的连接。
bindService()
方法会立即无值返回,但当Android系统创建客户端与服务之间的连接时,会对
ServiceConnection
调用
onServiceConnected()
,向客户端传递用来与服务通信的
IBinder
。
onBind()
方法来检索 IBinder
。系统随后无需再次调用onBind()
,便可将同一 IBinder
传递至任何其他绑定的客户端。
startService()
也启动了该服务)。
Binder
类并从
onBind()
返回它的一个实例来创建接口。客户端收到
Binder
后,可利用它直接访问
Binder
实现中乃至
Service
中可用的公共方法。
如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。
使用 Messenger 如需让接口跨不同的进程工作,则可使用Messenger
为服务创建接口。服务可以这种方式定义对应于不同类型
Message
对象的
Handler
。此
Handler
是
Messenger
的基础,后者随后可与客户端分享一个
IBinder
,从而让客户端能利用
Message
对象向服务发送命令。此外,客户端还可定义自有
Messenger
,以便服务回传消息。
这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger
会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。
Messenger
的方法实际上是以 AIDL 作为其底层结构。如上所述,
Messenger
会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。不过,如果您想让服务同时处理多个请求,则可直接使用AIDL。在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。
如需直接使用AIDL,您必须创建一个定义编程接口的 .aidl
文件。Android SDK工具利用该文件生成一个实现接口并处理 IPC的抽象类,您随后可在服务内对其进行扩展。
扩展 Binder 类
以下是具体的设置方法:
- 在您的服务中,创建一个可满足下列任一要求的
Binder
实例:- 包含客户端可调用的公共方法
- 返回当前
Service
实例,其中包含客户端可调用的公共方法 - 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法
- 从
onBind()
回调方法返回此Binder
实例。 - 在客户端中,从
onServiceConnected()
回调方法接收Binder
,并使用提供的方法调用绑定服务。
例如,以下这个服务可让客户端通过Binder
实现访问服务中的方法:
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);
}
}
getService()
方法,以检索
LocalService
的当前实例。这样,客户端便可调用服务中的公共方法。例如,客户端可调用服务中的
getRandomNumber()
。
点击按钮时,以下这个 Activity 会绑定到 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(); // 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; } }; }
使用 Messenger
以下是 Messenger
的使用方法摘要:
以下是一个使用 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.
*/
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();
}
}
客户端只需根据服务返回的 IBinder
创建一个 Messenger
,然后利用 send()
发送一条消息。例如,以下就是一个绑定到服务并向服务传递 MSG_SAY_HELLO
消息的简单 Activity:
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; } } }
Messenger
。然后,当客户端收到
onServiceConnected()
回调时,会向服务发送一条
Message
,并在其
send()
方法的
replyTo
参数中包含客户端的
Messenger
。
bindService()
会立即返回,“不会”
使
IBinder
返回客户端。要接收
IBinder
,客户端必须创建一个
ServiceConnection
实例,并将其传递给
bindService()
。
ServiceConnection
包括一个回调方法,系统通过调用它来传递
IBinder
。
因此,要想从您的客户端绑定到服务,您必须:
- 实现
ServiceConnection
。您的实现必须重写两个回调方法:
-
系统会调用该方法以传递服务的
onBind()
方法返回的IBinder
。 - Android系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“不会” 调用该方法。
onServiceConnected()
onServiceDisconnected()
-
系统会调用该方法以传递服务的
- 调用
bindService()
,传递ServiceConnection
实现。 - 当系统调用您的
onServiceConnected()
回调方法时,您可以使用接口定义的方法开始调用服务。 - 要断开与服务的连接,请调用
unbindService()
。如果应用在客户端仍绑定到服务时销毁客户端,则销毁会导致客户端取消绑定。更好的做法是在客户端与服务交互完成后立即取消绑定客户端。这样可以关闭空闲服务。如需了解有关绑定和取消绑定的适当时机的详细信息,请参阅附加说明。
例如,以下代码段通过扩展Binder 类将客户端与上面创建的服务相连,因此它只需将返回的 IBinder
转换为 LocalService
类并请求 LocalService
实例:
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;
}
};
客户端可通过将此 ServiceConnection
传递至 bindService()
绑定到服务。例如:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
bindService()
的第一个参数是一个Intent
,用于显式命名要绑定的服务(但 Intent 可能是隐式的)- 第二个参数是
ServiceConnection
对象 - 第三个参数是一个指示绑定选项的标志。它通常应该是
BIND_AUTO_CREATE
,以便创建尚未激活的服务。其他可能的值为BIND_DEBUG_UNBIND
和BIND_NOT_FOREGROUND
,或0
(表示无)。
以下是一些有关绑定到服务的重要说明:
- 您应该始终捕获
DeadObjectException
异常,它们是在连接中断时引发的。这是远程方法引发的唯一异常。 - 对象是跨进程计数的引用。
- 您通常应该在客户端生命周期的匹配引入 (bring-up) 和退出 (tear-down) 时刻期间配对绑定和取消绑定。例如:
- 如果您只需要在 Activity 可见时与服务交互,则应在
onStart()
期间绑定,在onStop()
期间取消绑定。 - 如果您希望 Activity 在后台停止运行状态下仍可接收响应,则可在
onCreate()
期间绑定,在onDestroy()
期间取消绑定。请注意,这意味着您的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当您提高该进程的权重时,系统终止该进程的可能性会增加。
- 如果您只需要在 Activity 可见时与服务交互,则应在
onResume()
和 onPause()
期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,您应该使发生在这些转换期间的处理保持在最低水平。此外,如果您的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一个 Activity 绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务。(Activity文档中介绍了这种有关 Activity 如何协调其生命周期的 Activity 转换。)
onStartCommand()
启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理 — Android系统会根据它是否绑定到任何客户端代您管理。
onStartCommand()
回调方法,则您必须显式停止服务,因为系统现在已将服务视为
已启动
。在此情况下,服务将一直运行到其通过
stopSelf()
自行停止,或其他组件调用
stopService()
为止,无论其是否绑定到任何客户端。
onUnbind()
方法时,如果您想在客户端下一次绑定到服务时接收
onRebind()
调用,则可选择返回
true
。
onRebind()
返回空值,但客户端仍在其
onServiceConnected()
回调中接收
IBinder
。下文图 1说明了这种生命周期的逻辑。