Bound Service

本文详细介绍了Android中绑定服务的原理及实现方法,包括如何通过Binder和Messenger实现本地和远程进程间的通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
   
}
}
 
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 ;
       
}
   
};
}
 

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 ();
   
}
}
 
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;
       
}
   
}
}
 
第三种方式,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()。
 
 The lifecycle for a service that is started and also allows binding.

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值