Xamarin.Android开发实践(七)

时间:2021-08-27 22:41:01

Xamarin.Android广播接收器与绑定服务

一、前言

学习了前面的活动与服务后,你会发现服务对于活动而言似乎就是透明的,相反活动对于服务也是透明的,所以我们还需要一中机制能够将服务和活动之间架起一座桥梁,通过本节的学习,你将会学到广播与绑定服务,这两种方式恰恰是解决上面问题的关键。

二、简单的广播接收器

实现一个最简单的广播接收器需要继承BroadcastReceiver类,并且还要实现OnReceive方法,我们可以在项目中新建一个MainReceiver类,然后写入如下代码:

1     public class MainReceiver : BroadcastReceiver
2 {
3 public override void OnReceive(Context context, Intent intent)
4 {
5
6 }
7 }

上面其实已经实现了一个简单的广播接收器,并且可以使用。我们还需要注册广播接收器,否则广播接收器就无法接收广播,所以我们需要在MainActivity.cs中注册这个广播接收器。当然为了能够接近现实,我们需要在OnResume中注册,在OnPause中注销。

首先我们在OnResume中注册

1         protected override void OnResume()
2 {
3 base.OnResume();
4 receiver = new MainReceiver();
5 RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
6 }

接着我们在OnPause中注销

1         protected override void OnPause()
2 {
3 base.OnPause();
4 UnregisterReceiver(receiver);
5 }

全部代码如下所示

 1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
2 public class MainActivity : Activity
3 {
4 private MainReceiver receiver;
5
6 protected override void OnCreate(Bundle bundle)
7 {
8 base.OnCreate(bundle);
9 SetContentView(Resource.Layout.Main);
10 }
11
12 protected override void OnResume()
13 {
14 base.OnResume();
15 receiver = new MainReceiver();
16 RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
17 }
18
19 protected override void OnPause()
20 {
21 base.OnPause();
22 UnregisterReceiver(receiver);
23 }
24 }

注册好了广播接收器,我们还需要一个能够发送广播的地方,既然我们说了这节重点解决的是服务与活动的通信,那么我们就实现一个服务来发送广播。为了能够贴近现实,我们的服务中将会新建一个线程,让这个线程发送一个广播给这个广播接收器。

 1     [Service]
2 public class MainService : Service
3 {
4 public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
5 {
6 new Thread(() =>
7 {
8 Thread.Sleep(1000);
9 var sintent = new Intent("xamarin-cn.main.receiver");
10 sintent.PutExtra("_str", "来自服务");
11 SendBroadcast(sintent);
12 }).Start();
13 return StartCommandResult.Sticky;
14 }
15
16 public override IBinder OnBind(Intent intent)
17 {
18 return null;
19 }
20 }

这里我们通过意图传递了一个参数,而在服务中发送广播的方法是SendBroadcast。其实我们可以看到在创建意图的时候传入了一个字符串,而这个字符串必须与注册广播接收器时指定的字符串一致,否则对应的广播接收器是无法接收到这个广播的,下面我们修改广播接收器的OnReceive方法,以便获取传递过来的字符串并显示。

1         public override void OnReceive(Context context, Intent intent)
2 {
3 string str = intent.GetStringExtra("_str");
4 new Handler().Post(() =>
5 {
6 Toast.MakeText(Application.Context, str, ToastLength.Long).Show();
7 });
8 }

其中我们通过意图的GetXXXX方法获取传递过来的参数,然后创建了一个Handler对象并使用Toast发送了一个提示,这里使用Handler是为了与UI线程同步。因为前面讲过只用UI线程才能够访问控件等等对象,而这里并没有RunOnUiThread方法,所以我们需要使用Handler对象的Post方法来实现。

最后有了服务还不行,我们还需要开启这个服务。当然我们依然还是要在OnResume中开启,在OnPause中暂停。

 1         protected override void OnResume()
2 {
3 base.OnResume();
4 receiver = new MainReceiver();
5 RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
6 StartService(new Intent(this, typeof(MainService)));
7 }
8
9 protected override void OnPause()
10 {
11 base.OnPause();
12 UnregisterReceiver(receiver);
13 StopService(new Intent(this, typeof(MainService)));
14 }

最后我们运行之后的结果如下所示

Xamarin.Android开发实践(七)

三、服务向活动发送消息

上面的例子我们仅仅只是打通了服务与广播接收器的通信,而我们今天的主题是服务与活动的双向通信,但是为了能够循序渐进学习,所以我们先学习了服务与广播接收器怎么通信,而这节我们将学习广播接收器如何与活动通信。

因为c#并没有java的部分语言的特性,所以我们没法直接通过匿名的方法创建一个继承自BroadcastReceiver类的实例,所以我们需要先创建一个继承自BroadcastReceiver的具体类,然后在其中定义活动需要响应的方法的委托(Action或者Func),这样我们可以在实例化这个具体类的同时将活动中的方法赋给广播接收器,这样广播接收器在OnReceive中就可以调用活动中的方法了,自然而言就打通了广播接收器与活动的通信。当然还有其他的方法,希望读者可以在留言中留下,以便更多的人进行学习。

首先修改MainReceiver类:

 1     public class MainReceiver : BroadcastReceiver
2 {
3 public Action<string> Alert;
4
5 public override void OnReceive(Context context, Intent intent)
6 {
7 string str = intent.GetStringExtra("_str");
8 if (Alert != null)
9 {
10 Alert(str);
11 }
12 }
13 }

在这里我们定义了一个委托(Action<string>  Alert)以便活动可以重写,同时还修改了OnReceive中的代码,从而使用活动的方法来显示提示,有了接口之后,我们就可以回到活动中进行重写了。因为广播被实例化的步骤是在OnResume中,所以我们这里直接给出这个方法中的代码(这里我们使用了一个TextView控件tv读者可以需要自行添加下)。

 1         protected override void OnResume()
2 {
3 base.OnResume();
4 receiver = new MainReceiver()
5 {
6 Alert = (s) =>
7 {
8 RunOnUiThread(() =>
9 {
10 tv.Text = s;
11 });
12 }
13 };
14 RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
15 StartService(new Intent(this, typeof(MainService)));
16 }

现在我们就打通了广播接收器与活动的桥梁,如果有多个方法也是一样的道理,我们现 在运行程序可以发现一切正常,下面笔者还要介绍另一种使用接口的方法,首先我们需要一个接口去规定活动需要实现哪些方法,然后在初始化广播接收器的同时将 活动的实例赋广播接收器的对应接口变量。下面我们将上面的例子改写,先定义个含有Alert的接口。

1     public interface IMainInterface
2 {
3 void Alert(string s);
4 }

然后让活动实现该接口

    public class MainActivity : Activity, IMainInterface
{
private MainReceiver receiver;
private TextView tv; public void Alert(string s)
{
RunOnUiThread(() =>
{
tv.Text = s;
});
}

接着我们修改广播接收器,公开一个该接收的属性,一遍在广播接收器被初始化的时候可以复制。

 1     public class MainReceiver : BroadcastReceiver
2 {
3 public IMainInterface mainInterface;
4
5 public override void OnReceive(Context context, Intent intent)
6 {
7 string str = intent.GetStringExtra("_str");
8 if (mainInterface != null)
9 {
10 mainInterface.Alert(str);
11 }
12 }
13 }

回到MainActivity中修改OnResume方法。

 1         protected override void OnResume()
2 {
3 base.OnResume();
4 receiver = new MainReceiver()
5 {
6 mainInterface = this
7 };
8 RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
9 StartService(new Intent(this, typeof(MainService)));
10 }

最后效果一样的,读者可以根据实际的情况选择。毕竟他们各自都有或多或少的缺点。

四、绑定服务

其实绑定服务就是将服务中的功能公开给活动,只有这样活动才能调用服务中的方法。而这一过程需要经过一个绑定。首先我们需要一个继承自Binder的类,这样才能将服务通过接口传递给活动。以下为继承自Binder的类,其中我们需要在初始化时将服务传入,然后公开一个方法将服务的实例返回。

 1     public class MainBinder : Binder
2 {
3 MainService mainService;
4
5 public MainBinder(MainService ms)
6 {
7 mainService = ms;
8 }
9
10 public MainService GetService()
11 {
12 return mainService;
13 }
14 }

接下来我们打开MainService文件,实现OnBind方法,并将上面类返回。

 1     [Service]
2 public class MainService : Service
3 {
4 public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
5 {
6 return StartCommandResult.Sticky;
7 }
8
9 public override IBinder OnBind(Intent intent)
10 {
11 return new MainBinder(this);
12 }
13 }

到此为止,服务这边已经做好了准备。既然是绑定自然不能通过简单的StartService方法开启,因为我们还需要OnBind返回的接口,否则活动无法与服务沟通。这就需要在活动中通过BindService方法进行绑定,但是该方法还需要一个实现了IserviceConnection接口的类,因为通过BindService方法进行绑定的操作是异步的,也就意味着不会阻塞当前调用该方法的线程,而是在服务成功开启并并且OnBind方法返回接口后会回调IserviceConnection中的方法,我们可以看下该接口的方法。

1     public interface IServiceConnection : IJavaObject, IDisposable
2 {
3 void OnServiceConnected(ComponentName name, IBinder service);
4 void OnServiceDisconnected(ComponentName name);
5 }

关于接口的方法,大致的解释如下:

OnServiceConnected:当服务中的OnBind方法返回接口后将回调该方法,并且通过service参数将OnBind返回的值传递给这个方法。

OnServiceDisconnected:当服务被关闭或者主动断开连接后回调该方法,如果我们利用这个方法重新恢复连接,或者发出异常并关闭对应的活动。

下面我们实现该接口

 1     public class MainServiceConnection : Java.Lang.Object , IServiceConnection
2 {
3 public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
4 {
5
6 }
7
8 public void OnServiceDisconnected(ComponentName name)
9 {
10
11 }
12 }

这里我们没有实现任何代码,该类与活动还没有关联起来,所以我们需要在活动中新建一个公开的变量去保存服务的接口。

1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
2 public class MainActivity : Activity
3 {
4 private TextView tv;
5 public MainBinder mainBinder;

接着我们就可以实现MainServiceConnection类了。

 1     public class MainServiceConnection : Java.Lang.Object , IServiceConnection
2 {
3 MainActivity mainActivity;
4 public MainServiceConnection(MainActivity ma)
5 {
6 mainActivity = ma;
7 }
8
9 public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
10 {
11 mainActivity.mainBinder = (MainBinder)service;
12 }
13
14 public void OnServiceDisconnected(ComponentName name)
15 {
16 mainActivity.mainBinder = null;
17 }
18 }

最后我们在活动中就可以进行绑定了。

 1     [Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
2 public class MainActivity : Activity
3 {
4 private IServiceConnection serviceConnection;
5 private TextView tv;
6 public MainBinder mainBinder;
7
8
9 protected override void OnCreate(Bundle bundle)
10 {
11 base.OnCreate(bundle);
12 SetContentView(Resource.Layout.Main);
13 tv = FindViewById<TextView>(Resource.Id.textView1);
14 }
15
16 protected override void OnResume()
17 {
18 base.OnResume();
19 serviceConnection = new MainServiceConnection(this);
20 BindService(new Intent(this, typeof(MainService)), serviceConnection, Bind.AutoCreate);
21 }
22
23 protected override void OnPause()
24 {
25 base.OnPause();
26 UnbindService(serviceConnection);
27 }
28 }

通过上面的步骤我们还不能看到实际的效果,下面我们需要在服务中实现一个简单的方法,只是返回一段字符串。

1         public string GetString()
2 {
3 return "来自服务";
4 }

然后在Main.axml中拖放一个按钮,并在活动中进行绑定。

 1         protected override void OnCreate(Bundle bundle)
2 {
3 base.OnCreate(bundle);
4 SetContentView(Resource.Layout.Main);
5 Button btn = FindViewById<Button>(Resource.Id.button1);
6 btn.Click += (e, s) =>
7 {
8 if (mainBinder != null)
9 {
10 string str = mainBinder.GetService().GetString();
11 Toast.MakeText(this, str, ToastLength.Long).Show();
12 }
13 };
14 }

这样我们就完成了活动调用服务中的方法,但是现实开发中。如果是耗时的任务。都是活动调用服务公开的方法后立即返回,然后服务在完成之后通过广播将处理的结果返回给活动,整个过程都是异步的。

Xamarin.Android开发实践(七)的更多相关文章

  1. Xamarin&period;Android开发实践(五)

    原文:Xamarin.Android开发实践(五) 一.服务的生命周期 服务与活动一样,在它的整个生命周期中存在着一些事件,下图可以很好解释整个过程以及涉及到的方法: 在真实的使用中,Service来 ...

  2. Xamarin&period;Android开发实践(四)

    原文:Xamarin.Android开发实践(四) Xamarin.Android下获取与解析JSON 一.新建项目 1.新建一个Android项目,并命名为为NetJsonList 2.右击引用,选 ...

  3. Xamarin&period;Android开发实践(三)

    原文:Xamarin.Android开发实践(三) 一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没 ...

  4. Xamarin&period;Android开发实践(二)

    原文:Xamarin.Android开发实践(二) 一.准备 开始学习本教程前必须先完成该教程http://www.cnblogs.com/yaozhenfa/p/xamarin_android_qu ...

  5. Xamarin&period;Android开发实践(一)

    原文:Xamarin.Android开发实践(一) 一.准备工作 1.创建一个空的解决方案,并命名为Phoneword 2.右击解决方案 新建->新建项目 并命名为Phoneword_Droid ...

  6. Xamarin&period;Android开发实践(十七)

    Xamarin.Android之定位 一.前言 打开我们手中的应用,可以发现越来越多的应用使用了定位,从而使我们的生活更加方便,所以本章我们将学习如何在Xamarin中进行定位的开发. 二.准备工作 ...

  7. Xamarin&period;Android开发实践(十五)

    Xamarin.Android学习之应用程序首选项 一.前言 任何App都会存在设置界面,如果开发者利用普通控件并绑定监听事件保存设置,这 一过程会非常的枯燥,而且耗时.我们可以看到Android系统 ...

  8. Xamarin&period;Android开发实践(十四)

    Xamarin.Android之ListView和Adapter 一.前言 如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xamarin去实现它,以及如何使用适配器和自定义适配器(本文 ...

  9. Xamarin&period;Android开发实践(十一)

    Xamarin.Android之使用百度地图起始篇 一.前言 如今跨平台开发层出不穷,而对于.NET而言时下最流行的当然还是 Xamarin,不仅仅能够让我们在熟悉的Vs下利用C#开发,在对原生态类库 ...

随机推荐

  1. mysql dumpfile与outfile函数的区别

    一直以为两个函数作用是相同的   经过简单测试发现还是有些区别的   如下表admin   mysql> select * from admin;   +-----+-----------+-- ...

  2. 转载:第四弹!全球首个微信小程序(应用号)开发教程!通宵吐血赶稿,每日更新!

    感谢大家支持!博卡君周末休息了两天,今天又回到战斗状态了.上周五晚上微信放出官方工具和教程了,推荐程序猿小伙伴们都去试一试,结合教程和代码,写写自己的 demo 也不错. 闲话不多说,开始更新! 第七 ...

  3. ACM&sol;ICPC ZOJ1009-Enigma 解题代码

    #include <iostream> #include <string> using namespace std; int main() { int strwide; cin ...

  4. PHP教程-防止网站被刷票的小技巧

    在Web开发中,投票模块会经常出现.这样就使得防止刷票,成了至关重要的技术.以下是兄弟连教育www.itxdl.cn总结的一些防止刷票方法: 1. IP限制 这是使用的最多,也是最广泛,不可少的刷票限 ...

  5. python&lpar;5&rpar;&colon;scipy之numpy介绍

    python 的scipy 下面的三大库: numpy, matplotlib, pandas scipy 下面还有linalg 等 scipy 中的数据结构主要有三种: ndarray(n维数组), ...

  6. 【QT】QPixmap在Label中自适应大小铺满

    KeepAspectRatio:设置pixmap缩放的尺寸保持宽高比. setScaledContents:设置label的属性scaledContents,这个属性的作用是允许(禁止)label缩放 ...

  7. &lt&semi;记录&gt&semi; HtmlHelper和 强类型页面

    HtmlHelper  路径生成 <!--普通写法--> <a href="/home/index">超链接</a> <!--利用Url类 ...

  8. python代码在IDE下调试设置命令行参数

    带命令行参数的代码在IDE下调试,需要把参数赋值,本文mark一下具体的命令行参数在代码中赋值方法. if __name__ == "__main__": sys.argv = [ ...

  9. 2879&period; &lbrack;NOI2012&rsqb;美食节【费用流】

    Description CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节.作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴.他很快就尝遍了美食节所有的美食.然而,尝鲜的欲望是难以满足的.尽 ...

  10. linux lvs 配置

    redhatAS4.2 安装集群LVS 环境描述: 本文在配置LVS时使用三台linux,一台做Directorserver (192.168.0.25) ,两台做realserver(192.168 ...