版权声明:

本账号发布文章均来自公众号,承香墨影(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

Android 广播内容全知道 | 掘金技术征文相关推荐

  1. Android 集成 Agora SDK 快速体验 RTC 版多人视频聊天|掘金技术征文

    RTC (Real-Time Communication) 作为实时通讯领域的"新贵",在互动直播.远程控制.多人视频会议.屏幕共享等领域广受好评,如果你还不了解 RTC ,Tak ...

  2. Agora SDK 在Android中的使用(在线视频通话)| 掘金技术征文

    首先声明本文是Agora SDK入门的小白文章 一.集成 1.注册账号创建项目 其中最重要的要数 App ID 了 2.下载Agora SDK 二.学会看示例代码(可跳过) 1.整体了解项目结构(1v ...

  3. Flutter入门三部曲(3) - 数据传递/状态管理 | 掘金技术征文

    Flutter数据传递 分为两种方式.一种是沿着数的方向从上向下传递状态.另一种是 从下往上传递状态值. 沿着树的方向,向下传递状态 按照Widgets Tree的方向,从上往子树和节点上传递状态. ...

  4. 一起来学习 WebRTC (篇一)| 掘金技术征文

    前言 作为一个认为啥都想懂一点的小开发,一直都对WebRTC很感兴趣,这个兴趣来源于几年前公司希望做一个即时通讯的小功能在APP上,不过最终由于项目最终需求更改而搁置.虽然如此,但是我还是了解了一些关 ...

  5. Flutter实现动画卡片式Tab导航 | 掘金技术征文

    前言 本人接触Flutter不到一个月,深深感受到了这个平台的威力,于是不断学习,Flutter官方Example中的flutter_gallery,很好的展示了Flutter各个widget的功能 ...

  6. 声网一起 KTV 功能初体验 | 掘金技术征文

    一起 KTV 是一个很重要,且能体现产品性能的功能,在学习声网 SDK 的时候,看到了一起 KTV 的 GitHub demo,并且官方也给出了相应的文档说明,在此也对我的学习过程做一个简单的记录. ...

  7. 2万5千字大厂面经 | 掘金技术征文

    以下面试题来自腾讯.阿里.网易.饿了么.美团.拼多多.百度等等大厂综合起来常考的题目. 如何写一个漂亮的简历 简历不是一份记流水账的东西,而是让用人方了解你的亮点的. 平时有在做一些修改简历的收费服务 ...

  8. Flutter完整开发实战详解(二、 快速开发实战篇) | 掘金技术征文

     作为系列文章的第二篇,继<Flutter完整开发实战详解(一.Dart语言和Flutter基础)>之后,本篇将为你着重展示:如何搭建一个通用的Flutter App 常用功能脚手架,快速 ...

  9. webrtc+canvas+socket.io从零实现一个你画我猜 | 掘金技术征文

    开场白 最近键盘坏了,刚好看到掘金有声网的技术征文,想整个键盘.于是就开始从零开始学习webrtc, 一开始看文档就是个素质三连.这么难啊,这咋整啊,这谁顶的住啊.于是就开始全网找资料,很幸运的在掘金 ...

最新文章

  1. QT在VS2013中的配置
  2. 使用C/C++发展Web系统开源
  3. php如何检测数组是否存在,php – 如何检查数组元素是否存在?
  4. 51 执行远程命令(Paramiko)
  5. 小月金嗓再现经典版迟来的爱
  6. Intel Edision入门
  7. mysql_根据身份证号识别性别、年龄、所在省份
  8. 百度MIP页规范详解 —— canonical标签
  9. Linux namespace - Docker 背后的故事
  10. JS获取当前屏幕宽高
  11. NumPy Essentials 带注释源码 五、NumPy 中的线性代数
  12. syn-proxy源码分析(1)
  13. 微宝球型机器人功能_《宇宙机器人 无线控制器使用指南》评测:与次世代的美好初恋...
  14. Linux远程服务器环境搭建
  15. 图解:为什么总有人能“躺着”挣钱?
  16. 文创雪糕火出圈!是真内卷还是真搞笑?
  17. 流言粉碎机:JAVA使用 try catch会影响性能
  18. Hook其他程序中的StringGrid的内容(内牛满面,终于找到了。)
  19. python下批量下载专利-欧洲专利局
  20. matlab2019使用仿真,光学仿真matlab中的handles怎么用 终于明白了

热门文章

  1. hibernate搭建
  2. 双网卡绑定-bond0
  3. 【原创】POSTGRESQL 分区表初次体验
  4. 用“ul+li”及css制作韩国风格菜单
  5. 搭建Eclipse+MyEclipse开发环境
  6. ccie对java有用吗,Java和ccie有啥区别
  7. python打破循环_python – 为什么“c.execute(…)”打破循环?
  8. python多个线程join_python-使用`thread.join()`时多线程冻结
  9. 微信多开txt_微信仅需3步操作,就能多开登录?手把手包教包会
  10. python0.1+0.2_为什么0.1+0.2=0.30000000000000004