Android--四大组件之BroadCastReceiver(生命周期、实现原理及使用等)

1. BroadCastReceiver是什么?

2. 广播类型

1). 有序广播
2). 无序广播

3. 生命周期

4. 实现原理

5. 使用方法

6. 权限问题(安全性)

7. LocalBroadcast

8. 注意事项

个人博客: IT老五
微信公众号:【IT老五(it-lao5)】,一起源创,一起学习!

BroadCastReceiver

BroadCastReceiver即广播接收器,用于监听/接收Android应用分发的各类广播并做出相应的响应。

应用场景:

  1. 监听系统事件:如开机广播,网络连接与断开,屏幕开启与关闭等
  2. 不同组件间通信(多个Activity/service,包括不同应用间)
  3. 多线程通信

广播类型

广播可分为无序广播和有序广播:

无序广播

无序广播即广播被发送后,BroadCastReceiver之间是无顺序,完全异步的,各个Receiver之间无关联。无序广播无法通过abortBroadcast终止,也无法使用setResult和getResult来传递处理结果。
无序广播直接通过Context.sendBroadcast()来发送。

有序广播

有序广播即广播发送后会按照优先级顺序被不同的广播接收器接收,优先级可以通过intent-filter的android:priority属性来设置,定义范围为-1000~1000,数值越大,优先级越高(如果优先级相同,1. 发送广播的进程会优先接收 2. 先注册的Receiver先接收)。
有序广播发送后,会被优先级最高的BroadCastReceiver接收,然后在处理完毕后依次被优先级较低的BroadCastReceiver接收,期间,BroadCastReceiver可以设置setResult将该广播的处理结果传递给下一个BroadCastReceiver,下一个Receiver通过getResult获取。有序广播被接收时,还可以通过abortBroadcast来终止,终止后,广播不会继续传递给其他Receiver。如:开发一个拦截短信的功能,我们将priority设置为1000,来最大限度的保证短信可以被我们的Receiver接收,在接收后,如果不想短信显示在系统短信列表中,我们可以abortBroadcast来终止传递,此时也就不会有短信提示音,也不会再短信收件箱中看到该短信。
有序广播需要通过Context.sendOrderedBroadcast来发送

粘性广播

其实将粘性广播与有序广播/无序广播放在一起讲并不是非常合适,因为对它们的定义并不是在同一个维度上。
粘性广播通过Context.sendStickyBroadcast()来发送,它与非粘性广播的区别是,在onReceiver中粘性广播不受10s限制(普通广播onReceiver在10s未处理完毕,会抛出ANR),粘性广播在10s后仍然存在,直至广播处理完毕。但需要注意的是,10s后,系统会将这个广播置为candidate状态,即可以被干掉的,当系统资源不足时,广播仍然可能会被丢弃。
使用粘性广播需要android.Manifest.permission.BROADCAST_STICKY权限

生命周期

广播的生命周期从调用开始到onReceiver执行完毕结束,需要注意的是,一般广播的生命周期都极短,需要在10s内处理完onReceiver中的所有工作,所以,一般不进行耗时长的工作,如果有耗时长的工作,应当通过Intent传递给Service进行处理。(注意,不要在onReceiver中开启线程进行耗时任务处理,否则,在10s后,该线程会变成空线程,从而导致任务的丢失。同样的,也不要使用bindService来绑定服务。)
值得注意的是,如果是在代码中动态注册的广播,如:在Activity注册,那么在Activity的onDestory中需要使用unregisterReceiver注销广播。

实现原理

在Android中,广播的出现是为了组件间的通信。其实在Android中,进程间通信有Binder,而同进程的通信方式就更多了,之所以使用广播,发送者与接受者都不需要知道对方的存在,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
Android中的广播使用了观察者模式:基于消息发布/订阅模式的事件驱动模型

广播模型中包含三个角色:

  1. 消息发布者(广播发布者)
  2. 消息中心(AMS,即Activity Manager Service)
  3. 消息订阅者(广播接收者)
  1. 广播接受者通过Binder机制在AMS中注册监听
  2. 广播发布者通过Binder机制向AMS发送广播
  3. AMS根据发送者的需求,在已注册表中获取到合适的广播接受者(根据Intent-filter,Permission)
  4. AMS将广播发送给合适的广播接受者的消息循环队列中
  5. 广播接受者通过消息循环获取到广播并回调onReceive()

整个广播发送与接收过程中,发送者与接收者是异步的,发送者不需要知道是否有接受者,也不需要知道接受者何时收到广播。

使用方法

广播的使用包括以下几个步骤:

  1. 自定义广播接收者:重写onReceiver()
  2. 注册广播接收者到消息中心(AMS):动态(代码中registerReceiver)或者静态注册(AndroidManifest.xml中申明)
  3. 定义及发送广播到消息中心(AMS):Context.sendBroadcast()、Context.sendOrderedBroadcast、Context.sendStickyBroadcast()
  4. AMS选择并发送广播给合适的广播接受者(根据Intent-filter,Permission)
  5. 广播接受者通过消息循环获取广播,并调用onReceive()进行处理

以上1~3步骤需要用户进行处理,4、5步骤由Android系统完成。

自定义广播接受者

广播接受者的自定义很简单,继承BroadcastReceiver基类并重写onReceive()即可:

// 继承BroadcastReceiver基类  
public class MyBroadcastReceiver extends BroadcastReceiver {
  // 复写onReceive()方法
  // 接收到广播后,则自动调用该方法
  @Override
  public void onReceive(Context context, Intent intent) {
    // 接收广播后的操作
  }
}
注册广播接收者

广播接受者的注册包含静态注册动态注册两种方式。
静态注册 即在AndroidManifest.xml中通过<receive>标签进行申明。该方式注册的广播为常驻广播,注册后,即使应用处于非运行状态,也可以接收到广播。
属性及实例:

<receiver 
  android:enabled=["true" | "false"]
  android:exported=["true" | "false"] // 此broadcastReceiver能否接收其他App的发出的广播,默认值是由receiver中有无intent-filter决定:如果有intent-filter,默认值为true,否则为false
  android:icon="drawable resource"
  android:label="string resource"
  android:name=".MyBroadcastReceiver "  // 继承BroadcastReceiver子类的类名
  android:permission="string" // 具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
    android:process="string">  // BroadcastReceiver运行所处的进程 默认为与app同进程,可以指定独立的进程。注:Android四大基本组件都可以通过此属性指定自己的独立进程
 <intent-filter>
    <action android:name="android.net.conn.BOOT_COMPLETED" /> //用于指定此广播接收器将接收的广播类型,可以配置多条,这里给出的示例是android系统开机广播
  </intent-filter>
</receiver>

动态注册 即在代码中通过registerReceiver(BroadcastReceiver receiver, IntentFilter filter)来动态注册广播,该方法包含两个参数,receiver即我们自己定义的MyBroadcastReceiver,IntentFilter即需要过滤的条件。动态注册的广播在应用停止运行后无法接收广播,比如在ActivityA中注册,则应当在ActivityA销毁前,使用unregisterReceiver(BroadcastReceiver receiver)来注销注册

MyBroadcastReceiver receiver=new MyBroadcastReceiver();  // 这里使用我们上面自定义的广播接受者
IntentFilter filter=new IntentFilter();  
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");  // 这里以网络连接状态变化为例
registerReceiver(receiver, filter);  
定义及发送广播

广播的发送有三种方法sendBroadcast(),sendOrderedBroadcast()和sendStickyBroadcast()
示例如下:

Intent intent = new Intent();  
intent.setAction("com.itlao5.broadcast");  
intent.putExtra("data", "data");  
sendBroadcast(intent); 

权限问题(安全性)

看到这里,应该都很清楚如何发送及接受广播。但是在此过程中是否会存在一些问题,比如我发送的广播只想让指定的广播接收者接收,或者我只想接收指定发送者发送的广播,这些该如何实现呢?
在上面广播注册一节中,我们可以看到有一个permission属性,没错,我们可以通过指定权限来实现上面的需求。

  1. 如何发送给指定的广播接受者?

在AndroidManifest.xml中,我们可以配置

<permission android:name = "com.android.permission.XXX"/> 

然后在发送广播时

sendBroadcast("com.android.XXX_ACTION", "com.android.permission.XXX");  

此时,就只有在广播接收的应用中在AndroidManifest.xml中添加对应的XXX权限,才能正常接收到该广播。

<uses-permission android:name="com.android.permission.XXX">></uses-permission>  
  1. 如何接收指定发送者的广播?

在这种情况下,需要在接受者app的<receiver> tag中声明一下发送者app应该具有的权限。
首先同上,在AndroidManifest.xml中定义新的权限SEND_XXX,例如:

<permission android:name="com.android.SEND_XXX"/>  

然后,在接受者app的Androidmanifest.xml中的<receiver> tag里添加权限SEND_XXX的声明

<receiver android:name=".MyBroadcastReceiver"   
         android:permission="com.android.permission.SEND_XXX">   
   <intent-filter>  
        <action android:name="com.android.XXX_ACTION" />   
   </intent-filter>  
</receiver> 

如此,该接受者便只能接收来自具有该SEND_XXX权限的应用发出的广播。要发送该广播,只需在发送者app的AndroidManifest.xml中也声明使用该权限即可,如下:

<uses-permission android:name="com.android.permission.SEND_XXX"></uses-permission>

LocalBroadcast

既然是说的广播,那么这里仅顺便提及一下LocalBroadcast。LocalBroadcast是本地广播,它的出现是为了应用内的广播传递。准确的说,LocalBroadcast与Broadcast不同,BroadCast基于Binder机制,而LocalBroadcast则是基于Handler机制。其机制的不同,导致他们的应用场景也不同,LocalBroadcast消耗资源少,安全性也相对来说更有保障,所以在应用内,更推荐使用LocalBroadcast。
这里简单提一下,有兴趣的可以看这篇博文:Android开发之局部广播的使用——LocalBroadcast

注意事项

  1. 动态注册的广播,在不需要使用时或者载体即将销毁时进行注销,即每一个registerReceiver需要有一个对应的unregisterReceiver
  2. 不要在广播接收器onReceive()中进行耗时操作,否则会引起ANR(10s)
  3. 不要在广播接收器onReceive()中开启异步任务,否则因为其生命周期的结束会出现空线程,导致任务丢失或者出现ANR等情况
  4. 耗时任务请开启service进行处理,且应当使用startService,而不应该使用bindService
  5. 应用内的广播尽量使用localBroadcast,因为其使用Handler,较Broadcast的Binder机制开销更小,且安全性更高
  6. 动态注册的广播优先度比静态注册高(当配置的优先级一致时),且可以控制其注册与注销,开销更小,所以能满足功能的情况下优先使用动态注册
  7. 如果接受不到自己发送的广播,请注意是否是因为权限问题
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容