Android 广播内容全知道

版权声明:

本公众号发布的所有文章,均属于原创,版权归本公众号所有。

未经允许,不得转载。

一、前言

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 会处于停止状态,现在能确定的就是两种状态:

  • 首次安装未启动过。

  • 在任务管理器中,被『强行停止』后。

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值