Android 广播内容全知道

版权声明:

本账号发布文章均来自公众号,承香墨影(cxmyDev),版权归承香墨影所有。

未经允许,不得转载。

一、前言

Broadcast 是 Android 四大组件之一,与传统意义上的电台广播类似,一个广播需要有一个发布者,以及任意多个接收者,并且它的特点也非常的明显,发布者只负责将广播发布出去,而不去关心接收者是否能正常接收到广播内容,也不关心接收者是如何处理广播的。以这种形式来达到发送者和接收者完全的解耦。

Broadcast 可以被大致分为三个角色:发送者、接收者(BroadcastReceiver)以及承载 Broadcast 的 Intent 对象。接下来就这三个角色进行单独讲解。

二、Broadcast 的类型

在 Android 中,为了适应不同的场景,Broadcast 可以被分为:

  • 无序广播。
  • 有序广播。
  • 本地广播。
  • Sticky 广播。

先来看看对于不同类型广播的特点。

1、无序广播

无序广播是完全异步的,通过 Context.sendBroadcast() 方法来发送,从效率上来看,还算是比较高的。

sendBroadcast() 方法中,还有一个第二个参数为 String 类型的重载方法,它是用来设定接收者的权限的,这个权限可以是系统权限,也可以是自定义的权限。

但是正如它的名称一样,无序广播对所有广播接收者(Receivers)而言,是无序的,也就是说,所有接收者无法确定接收时序的顺序,这样也导致了,无序广播无法被停止。当它被发送出去之后,它将通知所有这条广播的接收者,直到没有与之匹配的广播接收者为止。

2、有序广播

有序广播通过 Context.sendOrderedBroadcast() 方法来发送。有序广播和无序广播最大的不同,就是它可以允许接收者设定优先级,它会按照接收者设定的优先级依次传播。而高优先级的接收者,可以对广播的数据进行处理或者停止掉此条广播的继续传播。

想要设定有序广播的优先级,需要在 IntentFilter 中进行设定。在 AndroidManifest.xml 中,使用 android:priority 属性设置,在代码中,可以通过 IntentFilter.setPriority() 方法设定。这取决于 Broadcast 的注册方式。

可以看到,它的取值是有限定范围的,需要在 SYSTEM_LOW_PRIORITY 和 SYSTEM_HIGH_PRIORITY 之间。

可以看到,这样的 priority 的限定范围,就是在 -1000 ~ 1000 之间,而如果不对其进行设定,它的默认值为 0。

前面也提到,高优先级的接收者可以附加数据以及停止当前广播的传播。附加数据,可以通过 setResult() 方法来操作,同时也可以通过 getResult() 方法来获取比自己更高优先级的接收者设置的数据内容。而停止这条广播继续传播,可以调用 abortBroadcast() 方法。

3、Sticky广播

Sticky 广播和它的名字很像,它是一个具有粘性的广播。它被发出去之后,会一直滞留在系统中,直到有与之匹配的接收者,才会将其发出去。

Sticky 广播,使用 Context.sendStickyBroadcast() 方法进行发送广播。

从文档上可以看到,如果想要发送一个 Sticky 广播,需要具有 BROADCAST_STICKY 权限,这个可以在 AndroidManifest.xml 中进行注册,而如果没有此权限,则会抛出 SecurityException 异常。

对于系统而言,只会保留最后一条 Sticky 广播,并且会一直保留下去,也就是说,如果我们发送的 Sticky 广播不被取消,当有一个接收者的时候就会收到它,再来一个还是能收到。所有我们需要在合适的实际,调用 removeStickyBoradcast() 方法,将其取消掉。

从上面的方法文档中也可以看到 StickyBroadcast 已经被标记为 @Deprecated ,出于一些安全的考虑,已经将其标记为废弃,不再推荐使用。我们作为开发者,对于一些被标记为 @Depracated 的方法,使用起来还是需要谨慎的。

4、本地广播

前面介绍的广播,都是全局的,只要被发出去之后,所有注册了此广播的 App ,都可以接受到它,这样就带来了安全的隐患。而有时候,我们只是想让自己的 App 进程内使用,而无需将广播公布出去。那么就可以使用本地广播。

本地广播是 Android Support v4 : 21 版本才新增的广播类型,它使用 LocalBroadcastManager (以下简称 LBM)类来管理。

LocalBroadcast 的使用非常的简单,只需要将 Broadcast 的对应 API,替换为 LBM 为我们提供的 API 即可。

LBM 是一个单例对象,可以使用 LocalBroadcastManager.getInstance(Context ) 方法获取到。在 Context 中定义的和 Broadcast 相关的方法,在 LBM 中都有对应的 API 。非常有意思的是,LBM 为了区分异步和同步,使用了 sendBroadcast()sendBroadcastSync() 方法来做为区分。

三、注册广播接收者方式

在 Android 中 ,Broadcast 有两种注册方式:

  • AndroidManifest.xml 静态注册。
  • 代码中动态注册。

1、静态注册

在 AndroidManifest.xml 静态注册,是一种非常常用的注册方式。


这里注册了一个监听输入法改变的系统广播的 BroadcastReceiver。

2、动态注册

有一些情况下,我们因为一些限制,会需要使用到动态注册监听。

Context 中,为我们提供了动态注册广播接收者对应的 api。


这几行代码和上面静态注册的效果是一样的。但是既然是动态注册,可以在需要的时候进行广播接收者的注册,那么在不需要的时候就需要对其进行取消。

动态取消广播接收者的注册,需要使用 Context.unregisterReceiver() 方法,它需要一个 BroadcastReceiver 对象作为参数,这就是我们之前用于注册的 Receiver 对象。

3、动态注册和静态注册有什么区别?

理论上来说上来说,无论是使用静态注册,还是动态注册,当这个广播接收者被注册上之后,他们的后续操作是一样的。但是它们的注册时机却不同。

对于静态注册的接收者而言,实际上它在安装到设备中之后,就已经被注册上了,只要有与它匹配的广播被发出来,它就是可以被激活并处理广播的,而对于动态注册,只有当前 App 被启动,并且执行到 registerReceiver() 方法之后,才会完成注册,才能接收匹配的广播。

既然如此,Android 为了一些效率和安全的原因,规定一些系统广播无法被静态注册,例如:SCREEN_ON、SCREEN_OFF、TIME_TICK 等,这种触发频率比较高的系统广播。这些广播只允许动态注册,使用静态注册的方式虽然不会报错,但是也不会有效。

4、BroadcastReceiver

无论是使用那种注册方式,我们都需要有一个 BroadcastReceiver 对象,它是用于实际去处理广播的对象,它是一个抽象类,需要实现其内的方法 onReceive()。


使用 BroadcastReceiver 就可以接受到与之匹配的广播,广播是通过 IntentFilter 为过滤条件来匹配的,我们可以通过 onReceiver() 方法中的 intent 对象,来获取到接收到的广播的相关数据。

四、查缺补漏

到这里,基本上 Broadcast 的相关内容就讲解清楚了。但是实际使用中,有时候还是会碰到问题,这里单独用一个小结来分析碰到的问题,有新的问题会持续更新。

1、被停止的 App 无法接收 Broadcast

对于 Broadcast 的 api ,在 Android api level 11 (Android 3.1)之后有过调整。新增了两个 FLAG,用来控制 Broadcast 是否对处于停止状态的 App 起作用。

这两个 FLAG 为:

  • FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未启动的 App。
  • FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未启动的 App。

而加了这两个 flag 的版本之后,系统会默认向所有 Broadcast 的 Intent 增加 FLAG_EXCLUDE_STOPPED_PACKAGES 这个 flag,这样做是为了防止唤醒已经被停止的 App 来处理这个广播,这样可以节约很多不必要的资源浪费。可以看到 ,Android 为了优化效率,一直是在做努力的,在 最新的 Android O 上也做了大的优化。

而这样导致如果 App 处于停止的状态下,默认就不会接收到广播的。那么有没有办法解决这个问题?如果广播的发送方我们可以控制,只需要为广播增加 FLAG_INCLUDE_STOPPED_PACKAGES 即可,如果没发控制,暂时也没什么好的办法让被停止的 App 接收到这部分广播。

那么,我们还需要确定,什么情况下,App 会处于停止状态,现在能确定的就是两种状态:

  • 首次安装未启动过。
  • 在任务管理器中,被『强行停止』后。

当然不排除有一些管理软件会模拟『强行停止』的动作。

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

推荐阅读更多精彩内容