Android官方文档之Bound Services
Posted vanpersie_9987
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android官方文档之Bound Services相关的知识,希望对你有一定的参考价值。
绑定式Service在CS结构中扮演着Server的角色。绑定式Service允许其他组件(如Activity)绑定该Service、发送请求、接收响应、甚至IPC通信( interprocess communication)。绑定式Service通常服务于其他应用程序的组件、且没有明确的后台的概念(does not run in the background indefinitely)。
本文将介绍bound Service的相关内容,包括其创建、与其他应用组件如何绑定 等。有关Service的基础内容,您可以参考我翻译的官方文档:《Android官方文档之Services》;如需访问bound Service的官方原文,您可以点击这个链接:《Bound Services》。
绑定式Service基础(The Basics)
绑定式Service是一个继承于Service的类。它可以与其他应用交互。为了实现绑定Service,您必须重写onBind()方法。该方法返回一个IBinder接口,此接口是绑定式Service与其它应用组件交互的桥梁。
其它应用组件可调用bindService()
方法绑定Service。该方法需要传入的参数中包含一个实现了ServiceConnection
接口的对象。该对象监控着组件与Service的绑定状态(which monitors the connection with the service)。bindService()
方法并不返回数据,而一旦系统创建了组件与Service的连接,ServiceConnection
接口中的方法onServiceConnected()
将被回调,此时实现了IBinder
接口的对象将传递至组件中,这样便实现了Service与绑定组件的通信(to deliver the IBinder that the client can use to communicate with the service)。
Service可同时与多个组件绑定。然而Service仅在绑定的第一个组件时回调onBind()
方法以获得IBinder
接口对象,之后与该Service绑定的组件都传递的是同一个IBinder
接口对象,而且并不再回调onBind()
方法。
当Service与绑定它的最后一个组件解绑时,系统将该Service 销毁(destroy),当然若Service还使用start方式启动过(调用startService()
方法启动),则该Service并不会destroy。
创建bound Service时,最重要的就是实现onBind()
回调方法中的返回接口IBinder
,下面将介绍几种不同实现IBinder
接口的方式。
创建绑定式Service(Creating a Bound Service)
以下列举了三种实现IBinder
接口的方式:
继承Binder类(Extending the Binder class):
Binder
是一个实现了IBinder
接口的类。若Service只允许被本应用所在的进程访问(这是大多数情况),您需要继承Binder
类,并将该对象作为onBind()
方法的返回值。这样,与Service绑定的组件就可以通过该返回对象访问Binder
的继承类中的public方法、甚至是Service中的方法(to directly access public methods available in either the Binder implementation or even the Service)。若在您的应用程序中,Service仅作为一个在后台工作的组件,那么这种方式最好不过了。除非您需要Service进行跨进程通信。
- 使用Messenger(Using a Messenger):如需要使用
IBinder
进行跨进程通信,您应当为Service创建一个Messenger
对象。这样,Service可以定义一个Handler
对象以接受不同类型的Message
。Handler
是Messenger
的基础,它可以在客户端与IBinder
共享(This Handler is the basis for a Messenger that can then share an IBinder with the client),并允许使用Message
对象向Service端发送指令(allowing the client to send commands to the service using Message objects)。除此之外,亦可以在client端定义Messenger
,这样Service端可以回传信息。
- 使用AIDL(Using AIDL):AIDL(android Interface Definition Language)是Android接口定义语言的缩写。大多数应用程序并不应该使用AIDL创建bound Service,因为这需要应用程序有处理多线程的能力,而这会使应用程序变得复杂,在本文中并不打算具体介绍AIDL,如需访问有关AIDL的官方原文,您可以点击这个链接:《Android Interface Definition Language (AIDL)》。
继承Binder类(Extending the Binder class)
若您的Service仅是应用程序内部使用,并不需要跨进程通信,那么可以继承Binder
类。这样,与Service绑定的组件可以直接访问Service中的public方法。
!请注意:这种继承Binder
类的方式仅适用于Service与绑定的组件处于同一应用程序或进程的情况,当然这也是最普遍的情况。举例来说,在播放音乐应用程序中,可以使用这种方式将一个Activity与Service绑定,而Service用于在后台播放音乐。
创建方式:
- 在Service类中创建一个继承于
Binder
的内部类。在Service类中定义public方法,以便client端可以访问。在继承于Binder
的内部类中返回该Service实例。将该内部类实例作为onBind()
返回参数。 - 在客户端中的
onServiceConnected()
回调方法中接受Binder
对象,并访问Service中的public方法。
示例如下:
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);
}
}
在上例中,LocalBinder
提供了getService()
方法以获得LocalService
实例。这样,client端可以通过该实例访问Service中pubic方法。比如,client端可以访问LocalService
中的public方法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;
}
};
}
首先,在Activity的onStart()
回调方法中调用bindService()
绑定LocalService
,这时,LocalService
中的onCreate()
与onBind()
依次回调;接着,ServiceConnection
中的onServiceConnected()
方法回调,表示组件与Service已绑定,这时可以通过回传给onServiceConnected()
中的IBinder
接口对象获得LocalService
实例,一旦获得了该实例,便可以调用LocalService
中的public方法,如getRandomNumber()
方法。
!请注意:Service应在合适的时候与组件解除绑定,本例中应在onStop()
中解除与Service的绑定。
使用Messenger绑定Service(Using a Messenger)
当Service需要进行IPC通信时,应在Service中使用Messenger
。使用Messenger
的方式如下:
继承
Handler
类,并实现回调方法handleMessage()
,每当client端访问Service中的方法时,handleMessage()
都将回调(receives a callback for each call from a client)。需在Service中创建一个
Messenger
对象,构造该对象需传入一个Handler
参数。调用
Messenger
的getBinder()
返回一个IBinder对象,将该对象作为onBind()
回调方法的返回值。client端通过
onServiceConnected()
回传的IBinder参数,构造Messenger
对象,并将Message
信息传入Messenger
对象,发送给Service。Service在
Handler
的handleMessage()
方法中接收Message
信息。
按照如此方式,client端并没有显式调用Service中的方法,而是传递了Message
对象,并在Service的Handler中接收。
以下是Service端示例:
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();
}
}
以下是client端接收示例:
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;
}
}
}
本例中并未包含Service端向client端发送消息的逻辑,如需要Service答复client发送的消息,需在client端也创建一个Messenger
对象,当onServiceConnected()
方法被回调时,在send()
方法中传入replyTo
参数。
绑定一个Service(Binding to a Service)
绑定Service是一个异步过程(The binding is asynchronous):应用程序中的组件调用bindService()
绑定一个Service,bindService()
立即返回;接着系统回调Service的onBind()
方法,而client并不会接收到IBinder
参数,为了接收该参数,需要创建一个ServiceConnection
实例,并将该实例传入bindService()
中,系统会将IBinder
回传至ServiceConnection
的回调方法中(The ServiceConnection includes a callback method that the system calls to deliver the IBinder)。
!请注意:只有activities、services、content providers可以绑定Service, broadcast receiver不能绑定Service(you cannot bind to a service from a broadcast receiver)。
所以,绑定Service应按如下步骤:
实现
ServiceConnection
接口;实现
onServiceConnected()
方法:当client与Service建立绑定时,系统回调该方法,并将onBind()
返回的IBinder
参数回传至该方法中;实现
onServiceDisconnected()
方法:当绑定的Service意外终止时( unexpectedly lost),系统回调该方法,如Service被进程kill或Service崩溃(crashed)。系统若回调unBindService()
方法,将不会回调onServiceDisconnected()
方法。
- 调用
bindService()
,并传入ServiceConnection
的实现类对象;
- 当系统回调
onServiceConnected()
时,表示client与Service已绑定,此时可以访问Service中的public方法。
系统回调
unbindService()
,解除绑定。- !请注意:当client与Service绑定时,系统destroy掉client端,这将破坏绑定状态(destruction causes the client to unbind)。好的做法是只要client与Service交互完成,就解除绑定(to unbind the client as soon as it is done interacting with the service)。
下面的代码片段演示了如何绑定Service:
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;
}
};
下面演示了启动绑定的方式:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
其中第三个参数表示绑定的模式,通常为BIND_AUTO_CREATE
,表示当Service还尚未处于alive状态时创建该Service。其它可用的参数为BIND_DEBUG_UNBIND
、BIND_NOT_FOREGROUND
,若不打算指定模式,可传入0。
需要额外注意的地方(Additional notes)
当连接错误时,系统会抛出
DeadObjectException
异常,这也是在client端调用Service中的方法时可能抛出的唯一异常(This is the only exception thrown by remote methods)。binding 和 unbinding
应成对出调用。若当Activity在前台处于运行状态时,需要与绑定的Service交互,那么应在
onStart()
方法中bindService()
,在onStop()
中unbindService()
。若当Activity在后台处于stop状态时,那么应在
onCreate()
方法中bindService()
,在onDestroy()
中unbindService()
。此时系统将更易kill该Service。
!请注意:请不要在onResume() 和 onPause()
方法中绑定、解绑Service,因为这两个生命周期回调方法经常被回调,频繁的绑定与解绑会降低程序的执行效率。
管理Bound Service的生命周期(Managing the Lifecycle of a Bound Service)
当Service不再与任何Client绑定时,系统将回收该Service(除非Service也用Start方式启动了(将回调onStartCommand()
方法)),您无需手动管理一个纯bound Service的生命周期(you don’t have to manage the lifecycle of your service if it’s purely a bound service),系统会自动管理。
无论Service绑定了多少个client,若您还回调了onStartCommand()
方法,那么必须显式stop该Service,可以通过在Service中调用stopSelf()
方法、或在其他组件中调用stopService()
stop该Service。
若通过两种方式(start、bound)同时启动了一个Service,那么如果希望Service在下一次绑定该client时回调onRebind()
方法,应在onUnbind()
方法中返回true。按照这种方式,再次与该Service绑定的client仍可以在onServiceConnected()
方法中接收到回传的IBinder
参数。如下图所示:
以上是关于Android官方文档之Bound Services的主要内容,如果未能解决你的问题,请参考以下文章