Android广播接收器与绑定服务

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

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

public class MainReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{

}
}

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

protected override void OnResume()
{
base.OnResume();
receiver = new MainReceiver();
RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
}

接着我们在OnPause中注销

protected override void OnPause()
{
base.OnPause();
UnregisterReceiver(receiver);
}

全部代码如下所示

[Activity(Label = "BroadcastStudy", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
private MainReceiver receiver;

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
}

protected override void OnResume()
{
base.OnResume();
receiver = new MainReceiver();
RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
}

protected override void OnPause()
{
base.OnPause();
UnregisterReceiver(receiver);
}
}

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

[Service]
public class MainService : Service
{
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
new Thread(() =>
{
Thread.Sleep(1000);
var sintent = new Intent("xamarin-cn.main.receiver");
sintent.PutExtra("_str", "来自服务");
SendBroadcast(sintent);
}).Start();
return StartCommandResult.Sticky;
}

public override IBinder OnBind(Intent intent)
{
return null;
}
}

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

public override void OnReceive(Context context, Intent intent)
{
string str = intent.GetStringExtra("_str");
new Handler().Post(() =>
{
Toast.MakeText(Application.Context, str, ToastLength.Long).Show();
});
}

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

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

protected override void OnResume()
{
base.OnResume();
receiver = new MainReceiver();
RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
StartService(new Intent(this, typeof(MainService)));
}

protected override void OnPause()
{
base.OnPause();
UnregisterReceiver(receiver);
StopService(new Intent(this, typeof(MainService)));
}

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


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

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

public class MainReceiver : BroadcastReceiver
{
public Action<string> Alert;

public override void OnReceive(Context context, Intent intent)
{
string str = intent.GetStringExtra("_str");
if (Alert != null)
{
Alert(str);
}
}
}

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

protected override void OnResume()
{
base.OnResume();
receiver = new MainReceiver()
{
Alert = (s) =>
{
RunOnUiThread(() =>
{
tv.Text = s;
});
}
};
RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
StartService(new Intent(this, typeof(MainService)));
}

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

public interface IMainInterface
 {
 void Alert(string s);
 }

然后让活动实现该接口

public class MainActivity : Activity, IMainInterface
{
private MainReceiver receiver;
private TextView tv;

public void Alert(string s)
{
RunOnUiThread(() =>
{
tv.Text = s;
});
}

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

public class MainReceiver : BroadcastReceiver
{
public IMainInterface mainInterface;

public override void OnReceive(Context context, Intent intent)
{
string str = intent.GetStringExtra("_str");
if (mainInterface != null)
{
mainInterface.Alert(str);
}
}
}

回到MainActivity中修改OnResume方法。

protected override void OnResume()
{
base.OnResume();
receiver = new MainReceiver()
{
mainInterface = this
};
RegisterReceiver(receiver, new IntentFilter("xamarin-cn.main.receiver"));
StartService(new Intent(this, typeof(MainService)));
}

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

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

public class MainBinder : Binder
{
MainService mainService;

public MainBinder(MainService ms)
{
mainService = ms;
}

public MainService GetService()
{
return mainService;
}
}

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

[Service]
public class MainService : Service
{
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
return StartCommandResult.Sticky;
}

public override IBinder OnBind(Intent intent)
{
return new MainBinder(this);
}
}

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

 public interface IServiceConnection : IJavaObject, IDisposable
 {
 void OnServiceConnected(ComponentName name, IBinder service);
 void OnServiceDisconnected(ComponentName name);
 }

关于接口的方法,大致的解释如下:
OnServiceConnected:当服务中的OnBind方法返回接口后将回调该方法,并且通过service参数将OnBind返回的值传递给这个方法。
OnServiceDisconnected:当服务被关闭或者主动断开连接后回调该方法,如果我们利用这个方法重新恢复连接,或者发出异常并关闭对应的活动。

下面我们实现该接口

public class MainServiceConnection : Java.Lang.Object , IServiceConnection
{
public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
{

}

public void OnServiceDisconnected(ComponentName name)
{

}
}

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

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

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

public class MainServiceConnection : Java.Lang.Object , IServiceConnection
{
MainActivity mainActivity;
public MainServiceConnection(MainActivity ma)
{
mainActivity = ma;
}

public void OnServiceConnected(ComponentName name, Android.OS.IBinder service)
{
mainActivity.mainBinder = (MainBinder)service;
}

public void OnServiceDisconnected(ComponentName name)
{
mainActivity.mainBinder = null;
}
}

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

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


protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
tv = FindViewById<TextView>(Resource.Id.textView1);
}

protected override void OnResume()
{
base.OnResume();
serviceConnection = new MainServiceConnection(this);
BindService(new Intent(this, typeof(MainService)), serviceConnection, Bind.AutoCreate);
}

protected override void OnPause()
{
base.OnPause();
UnbindService(serviceConnection);
}
}

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

public string GetString()
 {
return "来自服务";
}

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

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
Button btn = FindViewById<Button>(Resource.Id.button1);
btn.Click += (e, s) =>
{
if (mainBinder != null)
{
string str = mainBinder.GetService().GetString();
Toast.MakeText(this, str, ToastLength.Long).Show();
}
};
}

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,563评论 18 139
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,835评论 6 13
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,266评论 25 707
  • 我是一个赌徒,88年生,今年29岁,湖南人,因为赌博,目前已跑路,有一本从2014年开始写但没写完的书—《无名小赌...
    无名小赌徒阅读 46,955评论 7 15
  • 目录后方(1)逃难途中 诸多艰难重险阻后方(2)沅水舟上 九曲九弯寻湖大后方(3)柳树湾里 多少曲曲复弯弯后方(4...
    范_纽文阅读 1,227评论 12 15