分类:C#、Android、VS2015;
创建日期:2016-03-03
如果服务是你的应用程序所私有的,即服务(Service)与客户端(Activity)都在同一个项目中(大部分应用程序的情况都是如此),这种服务称为本地服务。
对于本地服务,应该在继承自Binder的类中创建接口,并从重写的OnBind()方法中返回一个Binder的实例。客户端接收这个Binder对象并用它来直接访问Binder甚至Service中可用的公共(public)方法。
1、添加ch1701_main.xml文件
在layout文件夹下添加该文件,模板选择【XML】,因为布局比较简单,就不让它带设计界面了。当然也可以添加ch1701_main.axml文件让其带设计界面。
<?xml version="1.0" encoding="utf-8" ?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip" android:gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/ch1701_bind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="启动服务绑定"> <requestFocus /> </Button> <Button android:id="@+id/ch1701_call" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="调用服务提供的方法"> <requestFocus /> </Button> <Button android:id="@+id/ch1701_unbind" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="解除服务绑定" /> </LinearLayout>
2、创建服务—ch1701Service
要创建一个可被绑定的服务(bound service),必须在提供的服务中实现OnBind()回调方法,并在该方法中返回一个IBinder类型的对象,此对象定义了与服务进行通信的接口,该接口用于指明客户端如何与服务进行通信。
ch1701Service.cs文件的代码如下:
using Android.App; using Android.Content; using Android.OS; namespace MyDemos.SrcDemos { [Service] public class ch1701LocalService : Service { // 用于让客户端绑定的IBinder接口 private readonly IBinder binder; // 用于获取随机数 private readonly System.Random r = new System.Random(); public ch1701LocalService() { binder = new ch1701LocalBinder(this); } public override IBinder OnBind(Intent intent) { return binder; } // 声明让客户端调用的公共方法 public int GetRandomNumber() { return r.Next(100); } } // 因为本服务总是运行于与客户端相同的进程中,因此不需要用IPC进行处理。 public class ch1701LocalBinder : Binder { // 客户端可通过它调用服务提供的公共方法 public ch1701LocalService localService { get; private set; } public ch1701LocalBinder(ch1701LocalService service) { localService = service; } } }
(1)代码解释
(a)在MyService中包含可供客户端调用的公共方法。
既然是服务,就要在Service中包含可供客户端调用的公共方法,该例子仅仅用GetRandomNumber()方法来演示。当然,也可以通过这些公共方法方返回其它类的实例。
(b)从回调方法OnBind()中返回Binder的实例。
在客户端中,通过在回调方法OnServiceConnected()中接收Binder并调用服务提供的方法,即可实现对绑定的服务进行调用。
ch1701LocalBinder为客户端提供了localService属性,通过该属性得到ch1701LocalService的实例后,客户端就可以通过它调用服务中提供的公共方法(比如调用服务示例代码中的GetRandomNumber()方法)。
(c)注意事项。
服务和客户端之所以必须位于同一个应用程序中(同一个项目中),是为了让客户端能够正确转换(cast)返回的对象并调用对象的API。 另外,服务和客户端也必须位于同一个进程中,因为这种方式不能执行任何跨进程的序列化(marshalling)操作。要实现跨进程的序列化操作,可利用消息传递来实现。
(2)创建继承自Binder的类
要实现可被绑定的服务,除了创建服务外,还需要自定义一个继承自Binder的子类,在该子类中提供一个返回服务实例的方法。该子类是IBinder接口的默认实现。
Binder子类通过返回的Service实例响应客户端请求。
例如,在ch1701Service.cs文件中定义了一个名为ch1701ServiceBind的类,当客户端第1次连接服务时,Android会自动调用服务中提供的OnBind方法。这样以来,客户端就可以通过Service调用服务提供的公共方法了。
3、创建客户端—ch1701BindingActivity.cs
ch1701BindingActivity.cs文件的代码如下:
using Android.App; using Android.Content; using Android.OS; using Android.Widget; namespace MyDemos.SrcDemos { [Activity(Label = "【例17-1】绑定到本地服务")] public class ch1701BindingActivity : Activity { ch1701LocalService mService; ch1701ServiceConnection mConnection; bool mBound = false; protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); SetContentView(Resource.Layout.ch1701_main); mConnection = new ch1701ServiceConnection(this); var btnBind = FindViewById<Button>(Resource.Id.ch1701_bind); btnBind.Click += delegate { if (mBound == false) { // 绑定到LocalService Intent intent = new Intent(this, typeof(ch1701LocalService)); BindService(intent, mConnection, Bind.AutoCreate); } Toast.MakeText(this, "已绑定", ToastLength.Short).Show(); }; var btnCall = FindViewById<Button>(Resource.Id.ch1701_call); btnCall.Click += delegate { if(mBound==false) { Toast.MakeText(this, "请先绑定", ToastLength.Short).Show(); return; } // 注意如果该调用会导致某些操作的挂起,应该在单独的线程中调用它, // 以免降低activity的性能。 int num = mService.GetRandomNumber(); Toast.MakeText(this, "获取的值为: " + num, ToastLength.Short).Show(); }; var btnUnbind = FindViewById<Button>(Resource.Id.ch1701_unbind); btnUnbind.Click += delegate { // 与服务解除绑定 if (mBound == true) { UnbindService(mConnection); Toast.MakeText(this, "绑定已解除", ToastLength.Short).Show(); } }; } public class ch1701ServiceConnection : Java.Lang.Object, IServiceConnection { private ch1701BindingActivity activity; public ch1701ServiceConnection(ch1701BindingActivity activity) { this.activity = activity; } public void OnServiceConnected(ComponentName name, IBinder service) { ch1701LocalBinder binder = (ch1701LocalBinder)service; activity.mService = binder.localService; activity.mBound = true; } public void OnServiceDisconnected(ComponentName name) { activity.mBound = false; } } } }
(1)代码解释
该例子展示了客户端如何利用 ServiceConnection 和 OnServiceConnected() 回调方法绑定到服务。
(2)将客户端绑定到服务
客户端可以通过调用BindService() 方法来绑定服务。当客户端完成交互时,可调用UnbindService()来解除绑定。
调用BindService() 方法来绑定服务时,必须提供一个 ServiceConnection 的实现代码,用于监控与服务的连接。
BindService() 将会立即返回,没有返回值。但是Android系统在创建客户端与服务之间的连接时,会自动调用 ServiceConnection 中的 OnServiceConnected() 方法传递一个 IBinder ,客户端利用它即可与服务进行通信。
ch1701ServiceConnection类负责提供客户端和服务之间的调用接口,注意该类实现了IServiceConnection接口,由于ServiceConnection类是通过Java定义的,所以ch1701ServiceConnection类必须继承自Java.Lang.Object。
BindService方法实现的功能是:如果存在绑定,就使用Bind.AutoCreate值自动创建服务(Android 5.0及更高版本才有此选项)。该方法是一个异步调用,如果没有可绑定的服务该方法将返回false,如果有可绑定的服务,则通过回调将建立的连接发送到ch1701ServiceConnection类中的OnServiceConnected()方法。
MyServiceConnection类继承自ServiceConnection类,在这个类中,重写了OnServiceConnected方法。在这种情况下,IBinder是MyServiceBinder的一个实例。MyServiceBinder用于获取对MyService的引用,以便客户端可以利用它调用服务中定义的方法。
注意在MyServiceConnection的构造函数中,将MainActivity的实例传递到本类中声明的activity(例子中只是以MainActivity为例,但也可以是其他的Activity),这是为了可以在Activity中通过它自身定义的OnServiceConnected回调方法得到对服务的引用,这样以来,Activity就可以调用服务中提供的方法了。
(3)调用服务提供的方法
客户端通过Binder实例获取被绑定的服务的引用后,就可以调用服务中提供的方法了。在这个例子中,ch1701Service服务只定义了一个GetRandomNumber()方法。一旦Activity通过OnServiceConnected方法实现了绑定,这个Activity就可以利用binder来获取对服务的引用,随后就可以调用服务中提供的GetRandomNumber()方法了。
(4)取消服务绑定(Unbinding from the Service)
客户端(Activity)完成对服务的调用后,必须取消对该服务的绑定(即:在不使用服务时要及时关闭服务)。服务断开连接后,会自动调用ServiceConnection类的OnServiceDisconnected方法。
要解除被绑定的服务,客户端调用UnbindService方法即可,通过它可传递在绑定中使用的ServiceConnection实例。
如果没有Activity通过StartService方法调用该服务,也没有其他客户端绑定到该服务,则安卓系统将自动关闭该服务。