简述
继承Binder类
在onBind中创建一个继承Binder类,并返回。这样接到这个Binder的终端就可以直接使用它来访问Binder或者Service当中的共有方法。
当你的Service在幕后工作,并且仅仅被你自己的应用程序使用的时候,我们就推荐你使用这样的方法。如果你要使用你的服务可以被其他的应用程序访问或跨进程通信,你才需要用其他的方法。
使用Messenger
跨进程用这种方法。这种方式里面Service会定义一个Handler来处理不同类型的Message对象。Handler是Messager得以和client分享IBinder的基础,使得客户端可以使用Message对象向Service发送请求。客户端可以以自己定义一个Messenger,service也可以回送消息。
这是使用跨进程方式通信的最简单的办法,因为Messenger将所有的请求排队到一个单一的线程,不用费心去设计自己的service线程安全。
使用AIDL
Android接口定义语言,做了所有把对象分解为一个个操作系统能够识别的基础元素,并且可以使得他们跨进程。上一个Mesenger的技术,其实是使用AIDL来作为其基础的结构的。如之前提到的,Messenger创建一个所有客户端请求的队列,所以service可以一次接受一个请求。然而如果你想要service同时接收多个请求,那么你可以直接使用AIDL。在时你的Service要能够负载多线程并且线程安全。
想要直接使用AIDL,你必须创建一个 .ail文件,其中定义了编程的接口。Android sdk使用这个文件来生成一个抽象类,其中构建了结构并且处理IPC,在你的Service中拓展它。
提示:
大多数的app不应该使用AIDL来创建一个service,因为这需要多线程的负载能力,并且它的构建也更加复杂。所以,AIDL不适合于大多数的应用,并且在这里没有论述在你的service中怎样使用。如果你确定你想直接使用AIDL,查看AIDL的文档吧。
拓展Binder类
提示:比如一个音乐播放器需要将Activity和一个后台的Service进行绑定,就很适合使用这样的方法。
下面是开始的方法:
1.在你的Service中创建一个Binder的实例,至少包括以下一项:
- 客户端可以调用的公共方法。
- 返回当前service的实例,其中包含客户端可以调用的公有方法。
- 或者返回一个包含客户端可以调用的公有方法的service的内部类。
2.从onBind回调函数中返回Binder实例。
3.在客户端,从onServiceConnected回调方法中接收Binder对象,并且调用其中的方法来和service通信。
提示:之所以要在一个应用程序里面的原因,是这样的话客户端才可以获取到返回的对象并且调用API。Servie和客户端必须在相同的进程当中,因为这个技术不可以进程。
取个例子,下面是一个通过构建一个Binder使得客户端可以和servise通信的例子。
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方法,可以让客户端检索到LocalServce的实例。这使得客户端可以调用service中的公有方法。例如,客户端可以调用来自service的getRandomNumber方法。
下面是一个binds了LocalService的Activity,当按钮按下时候调用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; } };}
上面这个例子展示了客户端是怎样使用一个ServiceConnection的构造和回调onServiceConnected来绑定service的。下一个部分我们将介绍更多绑定service 的方法。
提示:在上面的例子里面,onStop方法将client从service上面解绑。客户端应该在合适的时机从service上面解绑,入宫在Additional Notes中讨论的那样。
更多的示例代码可以查看LocalSercie.java和LocalServiceAcitivities.java,在ApiDemos中。
使用一个Messenger
如果需要你的service跟远程的进程通信,那么你可以使用一个Messenger来提供你的service的接口。这个技术让你不使用AIDL就可以进行IPC。
下面是怎样使用Messenger的一个总结:
- 在service中构建一个Handler,其可以从客户端接受每一个调用的回调。
- Handler被用来创建一个Message对象(其实是一个Handler的引用)
- Messenger创建一个IBinder,也就是在onBind中service返回给客户端的那个。
- 客户端使用IBinder来初始化Messenger(它引用了服务的Handler),客户端使用这个Messsenger来给service发送Message对象。
- service在它的Handler中接收每一个Message。具体而言实在handleMessge方法中。
用这种方法,就没有了客户端能够调用的service的方法了。取而代之的是,客户端发送messages,也即是Message对象,service它的Handler中接收。
下面是使用Messenger接口的一个简单的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(); }}注意到这个例子中并没有膳食出srevice是如何应答客户端的,如果你要service去应答,那么你需要在客户端里面创建一个Messenger。那么当客户单接收到onServiceConnected的回调的时候,他就会想service发送Message,其中在send函数的reply参数中包含客户端的Messenger。
你可以在例子中看见怎样提供一种双工通信,在MessengerService.java(Service)和MessengerServiceActivities.java(client)的例子中。
绑定一个Service
应用组件(客户端)可以通过调用bindeService来绑定service。Android 系统随后调用service的onBinde方法,它返回一个用来和service交互的IBinder。绑定是异步的过程。bindService即时返回,并且不向客户端返回IBinder。为了接受IBinder,客户端必须创建一个ServiceConnection实例,并且把它传入给bindService。ServiceConnection中包含一个回调方法,系统调用它来传送IBinder。
提示:只有activity,service和content providers可以绑定一个service,你不能让一个broadcast receiver来绑定service。
所以,从你的客户端去绑定service,你必须做:
1.构建ServiceConnection
你的构建必须重写两个回调函数:
onServiceConnected()
系统通过调用它来传递service的onBind返回的IBinder。
onServiceDisconnected()
Android系统在和service 的连接以外地中断时候调用该函数。比如说当service崩溃了或者被杀掉了。当客户端解绑的时候,这个方法不会被调用。
2.调用bindService,将构建的ServiceConnection传入。
3.当系统调用你的onServieConnected的回调方法的时候,你可以开始和service通信 ,使用你在接口中定义的方法。
4.要断开和service的连接,就调用unbindeService()。
如果你的客户端在你的应用销毁客户端的时候仍然和service连接,那么销毁会使客户端解绑。在客户端完成和service的交互的时候解绑,这样做更好。
例如,下面的代码片展示了从客户单到service的连接,servise使用上面的extending the Binder 类的方法构建,所以所有他要做的事情就是获取返回的IBinder并传递到LocalService类中,并且请求LocalService实例。
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; }};有了ServiceConnetion,客户端可以通过传递以这个对象给bindService方法来绑定一个service。例如:
Intent intent = new Intent(this, LocalService.class);bindService(intent, mConnection, Context.BIND_AUTO_CREATE);- bindService的第一个参数是一个Intent,显式地说明了要绑定的service的名字。(尽管intent也可以是隐式)
- 第二个参数是ServiceConnection的对象。
- 第三个参数是一个标志位,它指示了绑定的选项。一般为BIND_AUTO_CREATE,用来创建一个service,如果这个service还没有创建。
- 其他的可能值为BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,或者是0表示什么也不做。
附加提示
下面是一些关于绑定服务的重点:
- 你应该一直注意捕捉DeadObjectException的异常,当connection断开的时候就可能捕获。这是远程方法唯一可能抛出的异常。
- 对象是被认为是跨进程的引用。
- bind和unbind应该成双成对,当你在客户端的声明周期里面建立匹配和断开匹配的时候。举个例子:
* 如果你只在你的activity可见的时候和service通信,你应该在onStart方法中bind,在onStop中unbind。
* 如果你想要你的activity在他stop在后台的时候,仍然接收回应,那么你可以在onCreate方法中bind,在onDestroy方法中unbind。你要意识到,这意味着你的activity需要在他运行的时候一直都使用service,包括在background的时候,所以如果service在其他的进程中,那么你增大了这个进程的负担,它很有可能被系统回收掉。
提示:请不要在onResume和onPause中来bind和unbind,因为这些调用在每一个生命周期转换的时候都会发生,而你应该把发生在转换过程中发生的进程数量控制到最小。并且如果你的应用中的多个activity都bind了同一个service,并且其中的两个activity之间发生跳转,那么当当前的(在pause的时候)activity解绑和下一个绑定(在resume时候)之间,服务将会被销毁并且重新创建。(Activity之间的转化过程如何协调,可以参见Activity文档)。
更多关于如何bind service的示例代码,请查看ApiDemos里面的RemoteService。
管理一个绑定的Service的生命周期
当一个service从所有的客户端解绑的时候,Android系统会销毁它( 除非他使用了onStartCommand启动的)。这样的话,你不需要管理你的service的生命周期,如果它只是单纯的一个绑定的service,android系统会帮你管理它,基于是够有客户端跟他绑定。
但是,如果你选择构建一个onStartCommand的回调方法,那么你必须显式地停止你的service,因为这个service现在被当做started(已启动)。在这种情况下,这个service会一直运行,除非他自己停止,比如说用stopSelf方法,或者另一个组件调用了stopService方法,无论其是否和客户端绑定。
除此之外,如果你的service启动了并且接受了bind,当系统调用了你的onUnbind方法时候,你可以选择返回true,如果你想在下次有客户端绑定service的时候被调用onRebind方法。onRebind返回一个空,但是客户单仍然在他的onSericeConnected回调方法中接收到IBinder。下面的图1表示了这种生命周期的逻辑。
关于service生命周期的更多信息,请看Service文档。