【Android】17.2 Activity与Local Service的绑定

分类:C#、Android、VS2015;

创建日期:2016-03-03

一、简介

如果服务是你的应用程序所私有的,即服务(Service)与客户端(Activity)都在同一个项目中(大部分应用程序的情况都是如此),这种服务称为本地服务。

对于本地服务,应该在继承自Binder的类中创建接口,并从重写的OnBind()方法中返回一个Binder的实例。客户端接收这个Binder对象并用它来直接访问Binder甚至Service中可用的公共(public)方法。

二、示例1运行截图

image  image

三、主要设计步骤

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方法调用该服务,也没有其他客户端绑定到该服务,则安卓系统将自动关闭该服务。

posted @ 2016-03-03 09:07  rainmj  阅读(540)  评论(0编辑  收藏  举报