Android学习--Service之Bound Service

一、基础

    其他应用组件可以绑定到Bound Service上,可以向其发送请求并获得响应,甚至可以通过它进行线程间通信(IPC)。Bound Service并不是在后台无限运行的,当没有组件绑定在它上面的时候,它就会被系统销毁(除非它还被startService()启动了)。
    一个service要实现绑定功能,必须要实现onBind()方法,该方法返回一个IBinder,是客户和service交互的接口。
    客户调用bindService()绑定到service上;注意,客户必须提供一个ServiceConnection的实现,当客户与service建立连接时,ServiceConnection中的onServiceConnected()方法会被调用,该方法接收由onBind()返回的IBinder,客户就是通过这个IBinder跟service通信。
    多个客户可以同时绑定到一个service上。但是,系统只在第一个客户绑定时调用onBind()方法、并返回一个IBinder;对于其他的客户,系统会向他们返回同一个IBinder,但是不会再调用onBind()方法。

二、创建Bound Service

     当创建一个Bound Service时,必须提供一个IBinder接口。

     定义IBinder有以下三种方式:
    *继承Binder类(Binder实现了IBinder接口)
    如果service只在你自己的应用里面使用、并且与客户运行在同一进程中,就应该使用这种方法创建IBinder;客户获得该Binder的实例后,就可通过它直接使用Binder中、甚至service中的方法。
    *使用Messenger
    如果IBinder接口要在不同进程间工作,可以用Messenger创建该接口。用这种方式,需要在service中定义一个Handler,用于处理Message;然后用这个Handler创建一个Messenger,调用该Messenger的getBinder()方法可得到一个IBinder,客户就可通过该IBinder向service发送Message了。另外,客户也可以定义自己的Messenger,这样,service就可以向客户返回Message了。
    这种方式是实现IPC的最简单的方法,因为Messenger将所有请求放进一个线程队列中,因此不需要考虑线程安全的问题。
    *使用AIDL(Android Interface Definition Language)
    AIDL是另一种实现IPC的方式。实际上,上面介绍的使用Messenger的方法,就是基于AIDL实现的。如上所述,Messenger会把所有的请求放进一个线程队列中,因此service每次只会接收到一个请求;如果service要同时处理多个请求,就可以使用AIDL实现。当然,使用AIDL时要求service能够处理多线程并需要考虑线程安全的问题,实现起来比Messenger方法复杂的多,所以可以选择时,优选Messenger方法。

    下面分别介绍前两种方法的具体实现:(关于AIDL的具体使用,见Android学习--Service之AIDL)

    方式一:继承Binder类
    注意,该方法只适用于客户与service处在同一应用和进程的情况。
    创建过程如下:
    1、在service中创建Binder的实例,该Binder应满足下列情形之一:
       *包含一个可被客户调用的方法
       *返回当前service的实例,且该service中包含能被客户调用的方法
       *返回service持有的某个类的实例,该类包含可被客户调用的方法
    2、在onBind()方法中返回这个Binder实例;
    3、在客户端的onServiceConnected()方法中得到这个Binder实例,并通过它提供的方法呼叫service。   

    例如:

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的实例,这使得客户可以调用service中的方法(如getRandomNumber()方法)。
    下面这个activity绑定到了LocalService上,并且当其中的button被点击时调用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;
        }
    };
}

    (上面的例子中没有明确何时解除对service的绑定,这是不合适的,所有客户都应该在合适的时间解除绑定。)

    方式二:使用Messenger
    如果service要与其他进程同信(IPC),可使用此方法。(如果service同时还要执行多线程任务,则应该使用AIDL)
    使用步骤如下:
    1、在service中创建一个Handler(用于接收客户的请求);
    2、用Handler的实例创建一个Messenger(它会引用到上述Handler);
    3、调用Messenger的getBinder()方法生成一个IBinder,并在onBind()方法中返回这个IBinder对象;
    4、客户用得到的IBinder对象实例化一个Messenger(它会引用到service中的Handler),客户可以用这个Messenger向service发送Message;
    5、service在其Handler的handleMessage()方法中接收客户发过来的Message。
   
    示例代码如下:

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

    客户所要做的就是,用service返回的IBinder创建一个Messenger,并调用其send()方法发送消息,如下:

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如何对客户进行响应。如果想让service进行响应,客户需要创建一个Messenger对象,当向service发送Message时,将创建的Messenger赋值给Message的replyTo成员变量;这样,service响应的消息就会被放到这个Messenger对象中。

三、绑定service  

    注意,只有Activity、Service、Content Provider可以绑定到service上,不能在BroadcastReceiver中绑定service。
    绑定过程是异步的。bindService()会马上返回,但是客户并不是通过该函数得到service返回的IBinder;要得到IBinder,客户必须创建一个ServiceConnection实例,并把它传递给bindService(),系统会调用ServiceConnection中的onServiceConnected()函数,用于传递service返回的IBinder。
    因此,客户要绑定到service,必须:
    1、实现ServiceConnection,并复写以下两个方法:
       onServiceConnected():系统会调用此方法传递由service的onBind()返回的IBinder。
       onServiceDisconnected():当跟service的连接被意外终止时被调用(例如,service崩溃或被意外杀死);如果连接的终止是由客户端接触绑定引起的,该方法不被调用。
    2、调用bindService()方法,并向它传递ServiceConnection的实现;
    3、当系统调用onServiceConnected()方法得到IBinder后,客户就可以通过该IBinder中的相应方法呼叫service;
    4、调用unbindService()解除绑定。(当客户被销毁时,系统会解除其对service的绑定,但是不应该依靠这种方式解除绑定;通常应该在完成了与service的交互、或绑定的activity处于paused状态时,调用unbindService()解除绑定、关闭service)

    具体的步骤可参见上面"创建Bound Service"部分的示例代码;
    但需注意bindService (Intent service, ServiceConnection conn, int flags)函数的三个参数:
    *service:是一个Intent,用于指定绑定到哪个service
    *conn:   创建的ServiceConnection实例
    *flags:  整数,指示绑定的方式,通常设为BIND_AUTO_CREATE,意思是,绑定时如果该service还未被创建,就创建它;还可以设为以下的值: 0, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY(各个值得具体含义参见关于"Context"抽象类的文档)。

其他的注意事项:
    1、当与远程service通信时,要记得捕获DeadObjectException,该异常在连接意外终止时抛出。如上述ActivityMessager代码中的sayHello()方法所示。
    2、Objects are reference counted across processes.(不理解!)
    3、应当成对的进行绑定和解除绑定的操作、并与客户的生命周期方法相对应,例如:
       *如果只是在activity可见时才与service交互,就应该在activity的onStart()方法中绑定service,在activity的onStop()方法中解除绑定;
       *如果要activity在不可见状态时仍能接收service的响应,应该在activity的onCreate()方法中绑定service,在activity的onDestroy()方法中解除绑定。(注意,这种情形意味着    activity的整个生命周期中都会用到service,如果该service处在另一个进程中,就会增加该进程的负担,导致系统杀死它的可能性提高)
    注意,通常不应该在activity的onResume()或onPause()方法中进行绑定service或解除绑定的操作,因为这两个函数被调用的比较频繁,应该在它们当中执行尽量少的操作。

四、管理Bound Service的生命周期

    当所有客户对其解除绑定时,系统会自动将其销毁(除非该service还被onStartCommand()启动了),因此对这种纯净的Bound Service,不需要对它的生命周期进行管理。但是,如果在该service中实现了onStartCommand()方法,该service会被认为是被"started";这时,即使没有客户绑定在这个service上,也必须调用stopSelf()或其他组件调用stopService()将其结束,这样该service才会被系统回收。(因此,在创建Bound Service时,如无必要,不要实现onStartCommand()方法。)
    另外,在一个被绑定的service同时被"started"时,可以选择让onUnbind()返回true,这样的话,再有客户绑定到该service上时,会调用其onRebind()方法(不再调用onBind()方法);虽然onRebind()返回值为void,客户仍然能从onServiceConnected()方法中得到IBinder对象。如下图中所示:

    Figure 1. The lifecycle for a service that is started and also allows binding.    
   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值