17 个必须掌握的 BroadcastReceiver 知识点「建议收藏」

Android

文章目录


文章目录

方便大家学习,我在 GitHub 上建立个 仓库


一、BroadcastReceiver

  • BroadcastReceiver,顾名思义就是“广播接收者”的意思,它是Android四大基本组件之一。
  • 这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息。
  • 它可以接收来自系统和应用的的广播。

1.1 什么是 BroadcastReceiver

什么是 BroadcastReceiver
  • 是四大组件之一, 主要用于接收 app 发送的广播
  • 内部通信实现机制:通过 android 系统的 Binder 机制.

1.2 广播分为两种

广播分为两种

1.2.1 无序广播

无序广播
  • 也叫标准广播,是一种完全异步执行的广播。
  • 在广播发出之后,所有广播接收器几乎都会在同一时刻接收到这条广播消息,它们之间没有任何先后顺序,广播的效率较高。
  • 优点: 完全异步, 逻辑上可被任何接受者收到广播,效率高
  • 缺点: 接受者不能将处理结果交给下一个接受者, 且无法终止广播.

1.2.2 有序广播

有序广播
  • 是一种同步执行的广播。
  • 在广播发出之后,同一时刻只有一个广播接收器能够收到这条广播消息,当其逻辑执行完后该广播接收器才会继续传递。
  • 调用 SendOrderedBroadcast() 方法来发送广播,同时也可调用 abortBroadcast() 方法拦截该广播。可通过 <intent-filter> 标签中设置 android:property 属性来设置优先级,未设置时按照注册的顺序接收广播。
  • 有序广播接受器间可以互传数据。
  • 当广播接收器收到广播后,当前广播也可以使用 setResultData 方法将数据传给下一个接收器。
  • 使用 getStringExtra 函数获取广播的原始数据,通过 getResultData 方法取得上个广播接收器自己添加的数据,并可用 abortBroadcast 方法丢弃该广播,使该广播不再被别的接收器接收到。
总结
  • 总结
  1. 按被接收者的优先级循序传播 A > B > C ,
  2. 每个都有权终止广播, 下一个就得不到
  3. 每一个都可进行修改操作, 下一个就得到上一个修改后的结果.

1.2.3 最终广播者

最终广播者
  • Context.sendOrderedBroadcast ( intent , receiverPermission , resultReceiver , scheduler , initialCode , initialData , initialExtras ) 时我们可以指定 resultReceiver 为最终广播接收者.
  • 如果比他优先级高的接受者不终止广播, 那么他的 onReceive 会执行两次
  • 第一次是正常的接收
  • 第二次是最终的接收
  • 如果优先级高的那个终止广播, 那么他还是会收到一次最终的广播

1.2.4 常见的广播接收者运用场景

广播接收者运用场景
  • 开机启动, sd 卡挂载, 低电量, 外拨电话, 锁屏等
  • 比如根据产品经理要求, 设计播放音乐时, 锁屏是否决定暂停音乐.

1.3 BroadcastReceiver 的种类

1.3.1 广播作为 Android 组件间的通信方式,如下使用场景:

对前一部分 “ 请描述一下 BroadcastReceiver ” 进行展开补充

BroadcastReceiver 使用场景
  • APP 内部的消息通信。

  • 不同 APP 之间的消息通信。

  • Android 系统在特定情况下与 APP 之间的消息通信。

  • 广播使用了观察者模式,基于消息的发布 / 订阅事件模型。广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。

  • BroadcastReceiver 本质是一个全局监听器,用于监听系统全局的广播消息,方便实现系统中不同组件间的通信。

  • 自定义广播接收器需要继承基类 BroadcastReceiver ,并实现抽象方法 onReceive ( context, intent ) 。默认情况下,广播接收器也是运行在主线程,因此 onReceiver() 中不能执行太耗时的操作( 不超过 10s ),否则将会产生 ANR 问题。onReceiver() 方法中涉及与其他组件之间的交互时,可以使用发送 Notification 、启动 Service 等方式,最好不要启动 Activity

1.3.2 系统广播

系统广播
  • Android 系统内置了多个系统广播,只要涉及手机的基本操作,基本上都会发出相应的系统广播,如开机启动、网络状态改变、拍照、屏幕关闭与开启、电量不足等。在系统内部当特定时间发生时,系统广播由系统自动发出。

  • 常见系统广播 Intent 中的 Action 为如下值:

常见系统广播 Intent 中的 Action 值
  1. 短信提醒:android.provider.Telephony.SMS_RECEIVED
  2. 电量过低:ACTION_BATIERY_LOW
  3. 电量发生改变:ACTION_BATTERY_CHANGED
  4. 连接电源:ACTION_POWER_CO
  • Android 7.0 开始,系统不会再发送广播 ACTION_NEW_PICTUREACTION_NEW_VIDEO ,对于广播 CONNECTIVITY_ACTION 必须在代码中使用 registerReceiver 方法注册接收器,在 AndroidManifest 文件中声明接收器不起作用。
  • Android 8.0 开始,对于大多数隐式广播,不能在 AndroidManifest 文件中声明接收器。

1.3.3 局部广播

局部广播
  • 局部广播的发送者和接受者都同属于一个 APP
  • 相比于全局广播具有以下优点:
  1. 其他的 APP 不会受到局部广播,不用担心数据泄露的问题。
  2. 其他 APP 不可能向当前的 APP 发送局部广播,不用担心有安全漏洞被其他 APP 利用。
  3. 局部广播比通过系统传递的全局广播的传递效率更高。
  • Android v4 包中提供了 LocalBroadcastManager 类,用于统一处理 APP 局部广播,使用方式与全局广播几乎相同,只是调用注册 / 取消注册广播接收器和发送广播偶读方法时,需要通过 LocalBroadcastManager 类的 getInstance() 方法获取的实例调用。

1.4 BroadcastReceiver 注册方式

BroadcastReceiver 注册方式

1.4.1 静态注册

AndroidManifest.xml 文件中配置。

<receiver android:name=".MyReceiver" android:exported="true">
    <intent-filter>
        <!-- 指定该 BroadcastReceiver 所响应的 Intent 的 Action -->
        <action android:name="android.intent.action.INPUT_METHOD_CHANGED"
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
  • 两个重要属性需要关注:
两个重要属性
  1. android: exported
    其作用是设置此 BroadcastReceiver 能否接受其他 APP 发出的广播 ,当设为 false 时,只能接受同一应用的的组件或具有相同 user ID 的应用发送的消息。这个属性的默认值是由 BroadcastReceiver 中有无 Intent-filter 决定的,如果有 Intent-filter ,默认值为 true ,否则为 false
  2. android: permission
    如果设置此属性,具有相应权限的广播发送方发送的广播才能被此 BroadcastReceiver 所接受;如果没有设置,这个值赋予整个应用所申请的权限。

1.4.2 动态注册

  • 调用 ContextregisterReceiver ( BroadcastReceiver receiver , IntentFilter filter ) 方法指定。

1.5 在 Mainfest 和代码如何注册和使用 BroadcastReceiver ? ( 一个 action 是重点 )

Mainfest 和代码如何注册和使用 BroadcastReceiver

1.5.1 使用文件注册 ( 静态广播 )

  • 只要 app 还在运行,那么会一直收到广播消息
使用文件注册 ( 静态广播 )
  • 演示:
  1. 一个 app 里: 自定义一个类继承 BroadcastReceiver 然后要求重写 onReveiver 方法
public class MyBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MyBroadCastReceiver", "收到信息,内容是 : " + intent.getStringExtra("info") + "");
    }
}
  1. 清单文件注册,并设置 Action , 就那么简单完成接收准备工作
<receiver android:name=".MyBroadCastReceiver">
    <intent-filter>
        <action android:name="myBroadcast.action.call"/>
    </intent-filter>
</receiver>

1.5.2 代码注册 ( 动态广播 )

  • 当注册的 Activity 或者 Service 销毁了那么就会接收不到广播.
代码注册 ( 动态广播 )
  • 演示:
  1. 在和广播接受者相同的 app 里的 MainActivity 添加一个注册按钮 , 用来注册广播接收者
  2. 设置意图过滤,添加 Action
//onCreate创建广播接收者对象
mReceiver = new MyBroadCastReceiver();              

//注册按钮
public void click(View view) {
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction("myBroadcast.action.call");
    registerReceiver(mReceiver, intentFilter);
}
  1. 销毁的时候取消注册
@Override
protected void onDestroy() {
    unregisterReceiver(mReceiver);
    super.onDestroy();
}

1.5.3 在另一个 app , 定义一个按钮, 设置意图, 意图添加消息内容, 意图设置 action( ... ) 要匹配 , 然后发送广播即可.

代码注册 ( 动态广播 )
public void click(View view) {
    Intent intent = new Intent();
    intent.putExtra("info", "消息内容");
    intent.setAction("myBroadcast.action.call");
    sendBroadcast(intent);
}
  • 运行两个 app 之后:
  1. 静态注册的方法: 另一 app 直接发广播就收到了
  2. 动态注册的方法: 自己的 app 先代码注册,然后另一个 app 直接发广播即可.-

1.6 BroadcastReceiver 的实现原理是什么?

  • Android 中的广播使用了设计模式中的观察者模式:基于消息的发布 / 订阅事件模型。
BroadcastReceiver 的实现原理
  • 模型中主要有 3 个角色:
  1. 消息订阅者( 广播接收者 )
  2. 消息发布者( 广播发布者 )
  3. 消息中心( AMS,即 Activity Manager Service

1.6.1 原理:

原理
  • 广播接收者通过 Binder 机制在 AMSActivity Manager Service ) 注册;

  • 广播发送者通过 Binder 机制向 AMS 发送广播;

  • AMS 根据广播发送者要求,在已注册列表中,寻找合适的 BroadcastReceiver ( 寻找依据:IntentFilter / Permission );

  • AMS 将广播发送到 BroadcastReceiver 相应的消息循环队列中;

  • 广播接收者通过消息循环拿到此广播,并回调 onReceive() 方法。

  • 需要注意的是:广播的发送和接受是异步的,发送者不会关心有无接收者或者何时收到。

1.7 本地广播

在这里插入图片描述
  • 本地广播机制使得发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播,则安全性得到了提高。

  • 本地广播主要是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

  • 开发者只要实现自己的 BroadcastReceiver 子类,并重写 onReceive ( Context context, Intetn intent ) 方法即可。

  • 当其他组件通过 sendBroadcast()sendStickyBroadcast()sendOrderBroadcast() 方法发送广播消息时,如果该 BroadcastReceiver 也对该消息“感兴趣”,BroadcastReceiveronReceive ( Context context, Intetn intent ) 方法将会被触发。

  • 使用步骤:

  1. 调用 LocalBroadcastManager.getInstance() 获得实例
  2. 调用 registerReceiver() 方法注册广播
  3. 调用 sendBroadcast() 方法发送广播
  4. 调用 unregisterReceiver() 方法取消注册

1.7.1 注意事项:

注意事项
  1. 本地广播无法通过静态注册方式来接受,相比起系统全局广播更加高效。
  2. 在广播中启动 Activity 时,需要为 Intent 加入 FLAG_ACTIVITY_NEW_TASK 标记,否则会报错,因为需要一个栈来存放新打开的 Activity
  3. 广播中弹出 Alertdialog 时,需要设置对话框的类型为 TYPE_SYSTEM_ALERT ,否则无法弹出。
  4. 不要在 onReceiver() 方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收器中是不允许开启线程的,当 onReceiver() 方法运行了较长时间而没有结束时,程序就会报错。

1.8 Sticky Broadcast 粘性广播

Sticky Broadcast 粘性广播
  • 如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的 Receiver ,这时接收者便无法接收到刚才的广播

  • 为此 Android 引入了 StickyBroadcast ,在广播发送结束后会保存刚刚发送的广播( Intent ),这样当接收者注册完 Receiver 后就可以继续使用刚才的广播。

  • 如果在接收者注册完成前发送了多条相同 Action 的粘性广播,注册完成后只会收到一条该 Action 的广播,并且消息内容是最后一次广播内容。

  • 系统网络状态的改变发送的广播就是粘性广播。

  1. 粘性广播通过 ContextsendStickyBroadcast ( Intent ) 接口发送,需要添加权限
  2. uses-permission android:name=”android.permission.BROADCAST_STICKY”
  3. 也可以通过 ContextremoveStickyBroadcast ( Intent intent ) 接口移除缓存的粘性广播

1.9 LocalBroadcastManager 详解

1.9.1 特点:

LocalBroadcastManager 特点
  1. 使用它发送的广播将只在自身APP内传播,因此你不必担心泄漏隐私数据;

  2. 其他 APP 无法对你的 APP 发送该广播,因为你的APP根本就不可能接收到非自身应用发送的该广播,因此你不必担心有安全漏洞可以利用;

  3. 比系统的全局广播更加高效。

1.9.2 源码分析 :

LocalBroadcastManager 源码分析
  1. LocalBroadcastManager 内部协作主要是靠这两个 Map 集合:MReceiversMActions ,当然还有一个 List 集合 MPendingBroadcasts ,这个主要就是存储待接收的广播对象。

  2. LocalBroadcastManager 高效的原因主要是因为它内部是通过 Handler 实现的,它的 sendBroadcast() 方法含义并非和我们平时所用的一样,它的 sendBroadcast() 方法其实是通过 handler 发送一个 Message 实现的;

  3. 既然它内部是通过 Handler 来实现广播的发送的,那么相比于系统广播通过 Binder 实现那肯定是更高效了,同时使用 Handler 来实现,别的应用无法向我们的应用发送该广播,而我们应用内发送的广播也不会离开我们的应用;

1.9.3 BroadcastReceiver 安全问题

BroadcastReceiver 安全问题
  • BroadcastReceiver 设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver 是存在安全性问题的 ( 恶意程序脚本不断的去发送你所接收的广播 ) 。为了解决这个问题 LocalBroadcastManager 就应运而生了。

  • LocalBroadcastManagerAndroid Support 包提供了一个工具,用于在同一个应用内的不同组件间发送 BroadcastLocalBroadcastManager 也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替 Handler 更新 UI

1.9.4 广播的安全性

  • Android 系统中的广播可以跨进程直接通信,会产生以下两个问题:
  1. 其他 APP 可以接收到当前 APP 发送的广播,导致数据外泄。
  2. 其他 APP 可以向当前 APP 放广播消息,导致 APP 被非法控制。
广播的安全性
  • 发送广播
  1. 发送广播时,增加相应的 permission ,用于权限验证。
  2. Android 4.0 及以上系统中发送广播时,可以使用 setPackage() 方法设置接受广播的包名。
  3. 使用局部广播。
  • 接受广播
  1. 注册广播接收器时,增加相应的 permission ,用于权限验证。
  2. 注册广播接收器时,设置 android:exported 的值为false。
  • 使用局部广播
  1. 发送广播时,如果增加了 permission
  2. 那接受广播的 APP 必须申请相应权限,这样才能收到对应的广播,反之亦然。

1.9.5 使用 BroadcastReceiver 的好处

使用 BroadcastReceiver 的好处
  1. 因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。

  2. 不用担心别的应用伪造广播,造成安全隐患。

  3. 相比在系统内发送全局广播,它更高效。

1.10 如何让自己的广播只让指定的 app 接收?

让自己的广播只让指定的 app 接收
  • 在发送广播的 app 端,自定义定义权限, 那么想要接收的另外 app 端必须声明权限才能收到.
  1. 权限, 保护层级是普通正常.
  2. 用户权限
<permission android:name="broad.ok.receiver" android:protectionLevel="normal"/>
<uses-permission android:name="broad.ok.receiver" />
  1. 发送广播的时候加上权限字符串
public void click(View view) {
    Intent intent = new Intent();
    intent.putExtra("info", "消息内容");
    intent.setAction("myBroadcast.action.call");
    sendBroadcast(intent, "broad.ok.receiver");
    //sendOrderedBroadcast(intent,"broad.ok.receiver");
}
  1. 其他app接收者想好获取广播,必须声明在清单文件权限
<uses-permission android:name="broad.ok.receiver"/>

1.11 广播的优先级对无序广播生效吗?

广播的优先级对无序广播生效
  • 优先级对无序也生效.

1.12 动态注册的广播优先级谁高?

动态注册的广播优先级谁高
  • 谁先注册,谁就高

1.13 如何判断当前的 BrodcastReceiver 接收到的是有序还是无序的广播?

判断当前的 BrodcastReceiver 接收到的是有序还是无序
  • onReceiver 方法里,直接调用判断方法得返回值
public void onReceive(Context context, Intent intent) {
    Log.d("MyBroadCastReceiver", "收到信息,内容是 : " + intent.getStringExtra("info") + "");
    boolean isOrderBroadcast = isOrderedBroadcast();
}

1.14 BroadcastReceiver 不能执行耗时操作

BroadcastReceiver 不能执行耗时操作
  • 一方面
  1. BroadcastReceiver 一般处于主线程。
  2. 耗时操作会导致 ANR
  • 另一方面
  1. BroadcastReceiver 启动时间较短。
  2. 如果一个进程里面只存在一个 BroadcastReceiver 组件。并且在其中开启子线程执行耗时任务。
  3. 系统会认为该进程是优先级最低的空进程。很容易将其杀死。

总结


  1. 本文应该是全网最全面的 BroadcastReceiver 知识总结了,如果有什么遗漏的地方,欢迎大家在评论区指出。
  2. 前前后后投入了大量时间来完成。希望大家通过本次阅读都能有所收获。
  3. 重点:关于 Android 的四大组件,到现在为止我才总结完 ActivityServiceBroadcastRecevier 等,有关事件分发、滑动冲突、新能优化等重要模块,后续我将进行全面总结,欢迎大家关注 _yuanhao 的简书 ,方便及时接收更新

码字不易,你的点赞是我总结的最大动力!


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

推荐阅读更多精彩内容

  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 3,023评论 0 8
  • 面试题总结 通用 安卓学习途径, 寻找资料学习的博客网站 AndroidStudio使用, 插件使用 安卓和苹果的...
    JingBeibei阅读 1,650评论 2 21
  • 现实中的广播:电台为了传达一些消息而发送广播,通过广播携带要传达的消息,群众只要买一个收音机,就可以收到广播了。 ...
    stevewang阅读 4,228评论 0 8
  • 本文转载自http://www.cnblogs.com/lwbqqyumidi/p/4168017.html 1....
    Ernest_Chang阅读 702评论 0 3
  • 今天天气有点冷,儿子和往常一样跟同学一起走路结伴回家。到家以后,跟我说今天老师布置了什么作业,然后就自觉的去...
    半度微凉1118阅读 140评论 0 0