一、前言

前面我们已经学了关于服务的很多知识,但是对于真实的开发那些远远不够,通过这节我们将学习其他类型的服务,比如前台服务、IntentService和消息服务。下面我们开始进入正题。

二、前台服务

顾名思义,就是拥有前台的优先等级。当然服务还是不可见的。因为前面我们介绍过Android系统会在低内存的情况下将一些长时间不用的应用关闭,如果还是不够,那么就会通过关闭服务服务来达到目的,然而对于某些应用而言,这样将会影响用户的正常使用。比如听音乐,我们基本上都会打开应用选择歌曲后将应用置为后台。但是你会发现通知栏中会存在这个通知并且无法移除,只有正确的退出这个应用了才会消失,而这节我们就要实现这个功能。

首先我们必须要用一个通知,通过这个通知我们的服务才能够变成前台服务,这里我们新建一个名为ForegroundService的服务,然后重写OnStartCommand方法。

  1. public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
  2. {
  3. var notify = new Notification(Resource.Drawable.Icon, "前台服务");
  4. var activityIntent = new Intent(this, typeof(MainActivity));
  5. var activityPintent = PendingIntent.GetActivity(this, , activityIntent, PendingIntentFlags.UpdateCurrent);
  6. notify.SetLatestEventInfo(this, "标题", "内容", activityPintent);
  7. StartForeground((int)NotificationFlags.ForegroundService, notify);
  8. return StartCommandResult.Sticky;
  9. }

很多代码都是我们在讨论通知的时候都已经掌握的了,既然是前台服务,自然最后发送这个方法是不同的,我们需要使用服务的StartForeground来发送这个通知,同时第一个参数也要设置为前台服务,这样我们就可以看到如图的结果了(需要在MainActivity的OnCreate方法中开启该服务)。

虽然已经是一个前台服务了,但是我们只能通过服务不断的更新这个通知,而无法接收用户的事件,下面我们还要实现一个自定义界面的通知,上面有一个Text和两个Button用户点击不同的按钮后将由服务去更新通知,从而改变Text中的值。

首先我们在Resources/layout/下新建一个NotificationLayout视图,并在其中写入如下的xml标记。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:p1="http://schemas.android.com/apk/res/android"
  3. p1:minWidth="25px"
  4. p1:minHeight="25px"
  5. p1:layout_width="match_parent"
  6. p1:layout_height="match_parent"
  7. p1:id="@+id/relativeLayout1">
  8. <TextView
  9. p1:text="0"
  10. p1:textAppearance="?android:attr/textAppearanceLarge"
  11. p1:layout_width="wrap_content"
  12. p1:layout_height="match_parent"
  13. p1:id="@+id/textView1" />
  14. <Button
  15. p1:text="显示1"
  16. p1:layout_width="wrap_content"
  17. p1:layout_height="match_parent"
  18. p1:layout_toRightOf="@id/textView1"
  19. p1:id="@+id/button1" />
  20. <Button
  21. p1:text="显示2"
  22. p1:layout_width="wrap_content"
  23. p1:layout_height="match_parent"
  24. p1:layout_toRightOf="@id/button1"
  25. p1:id="@+id/button2" />
  26. </RelativeLayout>

打开ForegroundService并在其中新建一个CreateNotify方法,并在其中写入如下代码。

  1. public Notification CreateNotify(string text)
  2. {
  3. notify = new Notification(Resource.Drawable.Icon, "前台服务");
  4. var sintent = new Intent(this, typeof(MainActivity));
  5. sintent.SetFlags(ActivityFlags.LaunchedFromHistory);
  6. notify.ContentView = new RemoteViews(PackageName, Resource.Layout.NotificationLayout);
  7. notify.ContentIntent = PendingIntent.GetActivity(this, , sintent, PendingIntentFlags.NoCreate);
  8.  
  9. var btn1Intent = new Intent(this, typeof(ForegroundService));
  10. btn1Intent.PutExtra("showBtn1", true);
  11. var btn1Pintent = PendingIntent.GetService(this, , btn1Intent, PendingIntentFlags.UpdateCurrent);
  12. notify.ContentView.SetOnClickPendingIntent(Resource.Id.button1, btn1Pintent);
  13.  
  14. var btn2Intent = new Intent(this, typeof(ForegroundService));
  15. btn2Intent.PutExtra("showBtn2", true);
  16. var btn2Pintent = PendingIntent.GetService(this, , btn2Intent, PendingIntentFlags.UpdateCurrent);
  17. notify.ContentView.SetOnClickPendingIntent(Resource.Id.button2, btn2Pintent);
  18.  
  19. notify.ContentView.SetTextViewText(Resource.Id.textView1, text);
  20. return notify;
  21. }

这里需要说明下,一旦通知发送出去了我们是无法同ContentViewSet去修改控件的,只能重新发送这个同时去更新旧的通知,所以笔者才需要一个单独的方法负责创建通知。上面的代码我们之前都已经学习过了,不理解的可以看这篇文件《Xamarin.Android通知详解》。笔者设置按钮的点击事件是打开服务本身,同时还通过Intent传递了一个参数,因为后面我们需要通过这些参数去区分哪个按钮按下了,同时还要注意PendingIntentGetService方法的第二个参数,我们两者都是0那么会造成按下按钮1和按纽2都传递同样的参数。下面我们在OnStartCommand中实现响应。

  1. public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
  2. {
  3. if (notify == null)
  4. {
  5. notify = CreateNotify("初始化");
  6. StartForeground((int)NotificationFlags.ForegroundService, notify);
  7. }
  8. bool isBtn1Click = intent.GetBooleanExtra("showBtn1", false);
  9. bool isBtn2Click = intent.GetBooleanExtra("showBtn2", false);
  10. if (isBtn1Click)
  11. {
  12. if (notify != null)
  13. {
  14. notify = CreateNotify("来自按钮1");
  15. StartForeground((int)NotificationFlags.ForegroundService, notify);
  16. }
  17. }
  18. else if (isBtn2Click)
  19. {
  20. if (notify != null)
  21. {
  22. notify = CreateNotify("来自按钮2");
  23. StartForeground((int)NotificationFlags.ForegroundService, notify);
  24. }
  25. }
  26. return StartCommandResult.Sticky;
  27. }

可以看到我们通过GetBooleanExtra获取了通过意图传递的参数,当然笔者这里的用法不同于前面的方式,我还传入了第二个参数,这样做的目的就是在意图中不存在该值的时候将会把第二参数返回,下面就是进行不同的判断从而更新通知。

最终运行结果如下所示:

按下“显示1”后

按下“显示2”后

至此我们就完成了前台服务的学习。

三、IntentService

很多时候我们都需要利用服务进行耗时的操作,势必需要创建新的线程去处理。但是普通的Service并不会主动创建而需要开发者自行在OnStartCommand中去创建,为此就繁衍出了IntentService类,它会为我们创建好线程去执行我们的代码,从而避免一些代码。但是我们不能重写OnStartCommand方法而应该是OnHandleIntent方法。比如下面的代码。

  1. [Service]
  2. public class MainIntentService : IntentService
  3. {
  4. protected override void OnHandleIntent(Android.Content.Intent intent)
  5. {
  6. Thread.Sleep();
  7. Toast.MakeText(this, "来自新线程" , ToastLength.Long).Show();
  8. }
  9. }

通过下面的截图我们可以看到OnHandleIntent中执行的代码是新建的一个线程

关于IntentService的使用非常简单。

四、通信服务

上一节关于绑定服务的学习中,活动必须确切的知道服务的类型才能使用,这样就加大了他们之间的耦合度,而通过本节我们将会学习如何通过消息机制将他们解耦,首先我们需要理解Handler类,它将会负责处理发送过来的消息,我们需要继承该类,并重写HandleMessage方法,我们新建一个MainHandler类并继承该类,然后重写。

  1. public class MainHandler : Handler
  2. {
  3. public override void HandleMessage(Message msg)
  4. {
  5. Toast.MakeText(Application.Context, "接收到的消息的what为" + msg.What.ToString() + " 内容为" + msg.Data.GetString("_str"), ToastLength.Short).Show();
  6. }
  7. }

这里我们仅仅只是简单的输出了消息的类型以及消息传递的参数,下面我们还需要一个服务将这个消息传递给活动。

  1. [Service]
  2. public class MessengerService : Service
  3. {
  4. Messenger messenger;
  5.  
  6. public MessengerService()
  7. {
  8. messenger = new Messenger(new MainHandler());
  9. }
  10.  
  11. public override Android.OS.IBinder OnBind(Android.Content.Intent intent)
  12. {
  13. return messenger.Binder;
  14. }
  15. }

这里我们还需要Messenger去封装MainHandler,因为MainHandler是无法在OnBind中直接返回的,只有Messenger的Binder属性可以,自然活动那边就需要接收这个接口,下面是IserviceConnection的实现。

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

这里的方式依然是使用之前我们讲述绑定服务时候的方法,只是在我们接收接口的时候是用Messenger的去封装的,这样就统一了。我们的活动只要有Messenger,并且对应的服务都满足这个接口那么我们的活动就可以灵活的绑定任意服务,使用他们的功能了,最后是MainActivity的代码(需要在Main.axml中拖拽两个按钮,以便发送消息给服务)。

  1. [Activity(Label = "OtherService", MainLauncher = true, Icon = "@drawable/icon")]
  2. public class MainActivity : Activity
  3. {
  4. public Messenger messenger;
  5.  
  6. protected override void OnCreate(Bundle bundle)
  7. {
  8. base.OnCreate(bundle);
  9. SetContentView(Resource.Layout.Main);
  10. BindService(new Intent(this, typeof(MessengerService)), new MessengerServiceConnection(this), Bind.AutoCreate);
  11. Button btn1 = FindViewById<Button>(Resource.Id.button1);
  12. btn1.Click += (e, s) =>
  13. {
  14. Message msg = Message.Obtain();
  15. Bundle b = new Bundle();
  16. b.PutString("_str", "消息1");
  17. msg.Data = b;
  18. msg.What = ;
  19. messenger.Send(msg);
  20. };
  21.  
  22. Button btn2 = FindViewById<Button>(Resource.Id.button2);
  23. btn2.Click += (e, s) =>
  24. {
  25. Message msg = Message.Obtain();
  26. Bundle b = new Bundle();
  27. b.PutString("_str", "消息2");
  28. msg.Data = b;
  29. msg.What = ;
  30. messenger.Send(msg);
  31. };
  32. }
  33. }

唯一要说的就是发送消息,我们需要实例化Messager(不是Messenger),设置它的what,如果我们还需要传递更多的参数我们可以实例化一个Bundle,然后通过其PutXXX方法赋值,最后赋给MessageData类型,最后要通过Messenger实例的Send方法发送这个消息,那么MainHandler就可以处理这个消息了。

下面是实际的运行图。

点击“发送消息1”按钮后

点击“发送消息2”按钮后

Xamarin.Android其他类型的服务的更多相关文章

  1. [译]:Xamarin.Android平台功能——位置服务

    返回索引目录 原文链接:Location Services. 译文链接:Xamarin.Android平台功能--位置服务 本部分介绍位置服务以及与如何使用位置提供商服务 Location Servi ...

  2. Xamarin.Android开发实践(八)

    Xamarin.Android其他类型的服务 一.前言 前面我们已经学了关于服务的很多知识,但是对于真实的开发那些远远不够,通过这节我们将学习其他类型的服务,比如前台服务.IntentService和 ...

  3. Xamarin.Android服务的实现

    一.服务的生命周期 服务与活动一样,在它的整个生命周期中存在着一些事件,下图可以很好解释整个过程以及涉及到的方法: 在真实的使用中,Service来还包含一个OnBind方法,并且必须要使用该方法,但 ...

  4. Xamarin.Android编译CPU类型选择方式

    Xamarin.Android编译CPU类型选择方式 在Xamarin.Android编译的时候,默认提供了5种CPU类型供大家选择.它们分别为armeabi.armeabi-v7a.arm64-v8 ...

  5. Xamarin Android设置界面提示类型错误

    Xamarin Android设置界面提示类型错误 错误信息:Integer types not allow (at ‘padding’ with value ’10’)Android界面属性的长度和 ...

  6. Android开发-API指南-Bound 类型的服务

    Bound Services 英文原文:http://developer.android.com/guide/components/bound-services.html 采集(更新)日期:2014- ...

  7. xamarin android如何将Java.Lang.Object类型转成C#类型

    问题起源 其实这个标题也可以换一个更准确一点,因为我遇到的问题是: xamarin android中的Class继承了Java.Lang.Object ,将json序列化成c#类型时发现无法赋值,序列 ...

  8. XAMARIN.ANDROID SIGNALR 实时消息接收发送示例

    SignalR 是一个开发实时 Web 应用的 .NET 类库,使用 SignalR 可以很容易的构建基于 ASP.NET 的实时 Web 应用.SignalR 支持多种服务器和客户端,可以 Host ...

  9. APP并非一个人在战斗,还有API—Xamarin.Android回忆录

    前言 一般来说,一个客户端APP并非独立存在的,很多时候需要与服务器交互.大体可分为两方面的数据,常规字符串数据和文件数据,因为这两种数据很可能传输方式不一样,比如字符串之类的数据,使用HTTP协议, ...

随机推荐

  1. [Android Pro] 自己动手编译Android源码(超详细)

    cp from : https://www.jianshu.com/p/367f0886e62b 在Android Studio代码调试一文中,简单的介绍了代码调试的一些技巧.现在我们来谈谈andro ...

  2. Log stash学习笔记(一)

    Logstash是一款开源的数据收集引擎,具备实时管道处理能力.简单来说,logstash作为数据源与数据存储分析工具之间的桥梁,结合 ElasticSearch以及Kibana,能够极大方便数据的处 ...

  3. [转]php判断一个数组是另一个数组的子集

    FROM : http://blog.csdn.net/lcion/article/details/8985220 今天完成一个算法的过程中,有几个需求模块,其中就有判断$a数组是否是$b数组的子集, ...

  4. [已解决]下载chromium源码 download_from_google_storage 无法下载文件

    当使用 gclient runhooks 后 出现下面的错误 File gs://chromium-gn/1088992877b3a13f25b61c8fc18e25296d8cab33 for sr ...

  5. Guava之ImmutableMap使用示例

    ImmutableMap 的作用就是:可以让java代码也能够创建一个对象常量映射,来保存一些常量映射的键值对. 分析以下情景,来具体讨论这个的好处. 假设现在有需求如下:根据数据库存的某个key字段 ...

  6. The Art of Deception

    前言 一些黑客毁坏别人的文件甚至整个硬盘,他们被称为电脑狂人(crackers)或计算机破坏者(vandals).另一些新手省去学习技术的麻烦,直接下载黑客工具侵入别人的计算机,这些人被称为脚本小子( ...

  7. vRealize Automation部署虚机如果出错怎么办?

    以下地方的日志可以查看: 1. Requests –> Choose my request -> View Detail –> Execution Information. 2. I ...

  8. 异常 Exception 堆栈跟踪 异常捕获 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. Web.config的Release版本和Debug版本不一样的奥秘

      VS编译完后,release版本的web.config(或者app.config) 为什么不一样那? 我们查看一下项目结构,会发现有两个版本的config文件存在: 打开web.Debug.con ...

  10. Sublime Es6教程1-环境搭建

    因为现在网上的教程都不靠谱,于是决定自己跳坑自己写,分为三块来玩: 一.环境搭建 二.语法讲解 三.项目实战 很多时候,你想搞一个东西,却因为环境没有搭建好,而不能很开森的探索未知的世界,多年的编程经 ...