Bound Services
A bound service 在client-server 接口中处于 server 端。A bound service 允许组件(如 activities)绑定到服务上,发送请求,接收响应,甚至能进行进程间通信(IPC),
A bound service 典型的存活于它在为别的组件服务时,并不会在后台持久的运行。
The Basics
为了提供绑定服务,你必须实现onBind()回调方法,他返回一个 IBinder object ,定义了程序的接口,客户端用它来与service交互。
bindService() 的时候,要提供一个 ServiceConnection 的实现。bindService() 会不带返回值的立即返回,但Android 系统会在 client 和 service
之间建立个链接,在ServiceConnection里面calls onServiceConnected(),会传递一个 IBinder 过来与service通信。
多个客户端可一次性链接到service,但只会在第一次client 绑定时会在 onBind()取得 IBinder,系统会传递相同的 IBinder 给额外绑定的cleints,
不需要再call onBind()。 当最后一个client unbinds 服务时,系统就会销毁掉服务。(除非服务是由 startService() 启动)
Creating a Bound Service
有三钟方式定义你的 IBinder 接口:
Extending the Binder class
如果你的service只为你的本地应用服务,没有跨进程的工作,那你就可以实现你自己的 Binder class 来为你的客户端提供直接访问service的公共方法。
Note: 这只适用于你的clients和service 都在同一个应用,同一个进程中。(这是相当普遍的)
步骤:
1.In your service , create an instance of Binder 包括:
* client能调用的public methods;
* 返回当前的service instance;
2.在 onBind() 返回这个 Binder的实例。
3.In the client,接收来自 onServiceConnected() 的Binder。
Note: service 和 client 必须在同一应用的原因是,client 能 cast 返回的对象,并能调用它的APIS。
它们也必须在同一进程,因为这技术不需要跨进程的工作。
For example:
publicclassLocalServiceextendsService{
// Binder given to clients
privatefinalIBinder mBinder= new LocalBinder();
// Random number generator
privatefinalRandom 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.
*/
publicclassLocalBinderextendsBinder{
LocalService getService(){
// Return this instance of LocalService so clients can call public methods
returnLocalService.this;
}
}
@Override
publicIBinder onBind(Intent intent){
return mBinder;
}
/** method for clients */
publicint getRandomNumber(){
return mGenerator.nextInt(100);
}
}
// Binder given to clients
privatefinalIBinder mBinder= new LocalBinder();
// Random number generator
privatefinalRandom 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.
*/
publicclassLocalBinderextendsBinder{
LocalService getService(){
// Return this instance of LocalService so clients can call public methods
returnLocalService.this;
}
}
@Override
publicIBinder onBind(Intent intent){
return mBinder;
}
/** method for clients */
publicint getRandomNumber(){
return mGenerator.nextInt(100);
}
}
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 ;
}
};
}
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 ;
}
};
}
Using a Messenger
如果你的service需要与远程进程通信,可以为service 提供 messenger 作为接口。这避免使用AIDL。
这是最简单实现 IPC 通信的方法,因为 Messenger队列的所有请求都是在一个单线程中的,所以你不需要把你的service设计成thread-safe。
步骤:
* service 实现个 Handler 去接收来自client的调用。
* Handler 用来创建一个 Messenger 对象(引用指向Handler)。
* Messenger 创建一个 IBinder that 来自service 的 onBind() 方法。
* clients 用这个 IBinder 实例化那个引用了service's Handler 的Messenger 对象,client就能用它send Message object to the servcie。
* service 在它的 Handler 就收每个 Message,在 handleMessage() 方法中处理。
a simple example that uses a Messenger interface:
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 ();
}
}
/** 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 ();
}
}
publicclassActivityMessengerextendsActivity{
/** 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.
*/
privateServiceConnection mConnection= new ServiceConnection(){
publicvoid 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;
}
publicvoid 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;
}
};
publicvoid 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
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView (R.layout.main);
}
@Override
protectedvoid onStart(){
super.onStart();
// Bind to the service
bindService (newIntent(this,MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protectedvoid onStop(){
super.onStop();
// Unbind from the service
if(mBound){
unbindService (mConnection);
mBound =false;
}
}
}
/** 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.
*/
privateServiceConnection mConnection= new ServiceConnection(){
publicvoid 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;
}
publicvoid 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;
}
};
publicvoid 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
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView (R.layout.main);
}
@Override
protectedvoid onStart(){
super.onStart();
// Bind to the service
bindService (newIntent(this,MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protectedvoid onStop(){
super.onStop();
// Unbind from the service
if(mBound){
unbindService (mConnection);
mBound =false;
}
}
}
第三种方式,AIDL,下篇再介绍。
Binding to a Service
绑定的过程是异步的。bindService() 会马上返回,但不会为client 返回 IBinder。
client 必须创建个 ServiceConnection ,把它传给 bindService(),在它的回调onServiceConnected()中,系统会把IBinder传递过来。
Note:只有 activites,services,content providers 能绑定服务-------你不能通过 broadcast receiver 来绑定服务。
虽然,当client销毁时,服务也会被 unbind 掉,但你总是应该 unbind service when 你和service交互完后。
Additional notes
- 你应该总是检查 DeadObjectException exceptions,当连接broken时,这是唯一会被远程方法thrown的exception。
- 在跨进程中,对象是引用计数的。
- binding 和 unbinding 在客户端的生命周期应该总是成对出现:
* 如果你想在 activity visible 时,和servcie进行交互,你应该在 onStart() bind service, onStop() unbind service。
* 如果你想你的 activity 在stopped在后台时仍能接收响应,你应该在 onCreate() bind servcie , onDestroy() unbind service。
请意识到,这种实现,你的activity 要持续使用service在它的整个生命周期(甚至在后台),如果service在另一个进程,这会加重进程的负担,
系统很可能会把它 kill 掉。
Note:通常不应该在 onResume() bind servcie ,在onPause() unbind service。因为这些callbacks频繁发生在生命循环中转移中,
所以,你应该在这过程中,尽量保持最小化的操作。另外,在多个activities绑定到同一个service时,在activities进行转移时,
由于当前的activity unbinds service 在下一个 binds service 之前,会导致服务的销毁和重新建立。
Managing the Lifecycle of a Bound Service
如果实现了 onStartCommand() 方法,你必须明确的stop the service,因为它会被认为是 started 的service。这种服务,直到stopSelf(),
或 别的组件call stopServcie() 停止掉它,否则service是不会停止的。不管service绑定了多个客户端。
而纯粹的 bound service 系统会帮你管理它的生命周期。
onUnbind() 时返回true,下次进行绑定时,就会调用 onRebind() ,而不是调用 onBind()。